function 数据双向绑定(){
// 数据劫持和发布订阅一起实现的数据双向绑定
// 把一个普通 Javascript 对象传给 Vue 实例的 data 选项,
// Vue 将遍历此对象所有的属性,
// 并使用 Object.defineProperty 把这些属性全部转为 getter/setter。
// Object.defineProperty 是仅 ES5 支持,且无法 shim 的特性,
// 这也就是为什么 Vue 不支持 IE8 以及更低版本浏览器的原因。
Object.defineProperty
var Book = {}
var name = '';
Object.defineProperty(Book, 'name', {
set: function (value) {
name = value;
console.log('你取了一个书名叫做' + value);
},
get: function () {
return '《' + name + '》'
}
})
Book.name = 'vue权威指南'; // 你取了一个书名叫做vue权威指南
console.log(Book.name); // 《vue权威指南》
// 它是es5一个方法,可以直接在一个对象上定义一个新属性,或者修改一个已经存在的属性,
// 并返回这个对象,对象里目前存在的属性描述符有两种主要形式:
// 数据描述符和存取描述符。
// 数据描述符是一个拥有可写或不可写值的属性。
// 存取描述符是由一对 getter-setter 函数功能来描述的属性。
// 描述符必须是两种形式之一;不能同时是两者
}
function newVue(){
// new 关键字在 JavaScript 中代表实例化一个对象,而 Vue 实际上是一个类,就是一个 Function。 源码在 src/core/instance/index.js 中
function Vue (options) {
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
this._init(options)
}
// 可以看到 Vue 通过 new 关键字初始化,然后会调用 this._init 方法, 在 src/core/instance/init.js 中:
Vue.prototype._init = function (options?: Object) {
const vm: Component = this
// a uid
vm._uid = uid++
let startTag, endTag
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
startTag = `vue-perf-start:${vm._uid}`
endTag = `vue-perf-end:${vm._uid}`
mark(startTag)
}
// a flag to avoid this being observed
vm._isVue = true
// merge options
if (options && options._isComponent) {
// optimize internal component instantiation
// since dynamic options merging is pretty slow, and none of the
// internal component options needs special treatment.
initInternalComponent(vm, options)
} else {
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
}
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
initProxy(vm)
} else {
vm._renderProxy = vm
}
// expose real self
vm._self = vm
initLifecycle(vm)
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate')
initInjections(vm) // resolve injections before data/props
initState(vm)
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
vm._name = formatComponentName(vm, false)
mark(endTag)
measure(`vue ${vm._name} init`, startTag, endTag)
}
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
// Vue 初始化只要就是干了这几件事情,合并配置,初始化生命周期,初始化事件中心,初始化渲染,初始化 data、props、computed、watcher 等。
// 初始化的最后,检测到如果有 el 属性,则调用 vm.$mount 方法挂载 vm,挂载的目标就是把模板渲染成最终的 DOM。
}
function v-model原理(){
// 其实在vue中,在使用v-model绑定数据之后,既绑定了数据,又添加了事件监听,这个事件就是input事件
// <input v-model="something"></input>
// 这不过是以下示例的语法糖
// <input
// v-bind:value="something"
// v-on:input="something = $event.target.value">
/* 这就解释了为什么在input元素使用v-model绑定数据后,可以实现双向绑定,因为输入的时候会触发元素的input事件 */
}
computed(){
// 计算属性将被混入到vue实例中,
// 所有的getter和setter的this上下文自动地绑定为vue实例
// 就算属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算
}
watch(){
// 一个对象,键是需要观察的表达式,值是对应回调函数
}
组件通信(){
1.父子组件通信--->父向子{
// 父组件通过v-model绑定index到子组件上,而通过v-model绑定的数据在子组件中默认的key是value,
// 所以在props上用value取值,最后通过点击事件执行$emit,而$emit上触发的事件是input,
// 前面我们说过v-model绑定的事件是input,从而在子组件上触发了父组件的input事件,而在触发事件的时候可以传值,
// 所以就实现了父子组件数据双向绑定,如果用的是v-bind,还需要自己去定义事情,所以使用v-model可以减少代码量
// 父组件:
// 1.import son from './son.js' 引入子组件 son
// 2.在components : {"son"} 里注册所有子组件名称
// 3.在父组件的template应用子组件, <son></son>
// 4.如果需要传递数据给子组件,就在template模板里写 <son :num="number"></son>
// 子组件:
// 1.用props接受数据,就可以直接使用数据
// 2.子组件接受到的数据,不能去修改。如果你的确需要修改,可以用计算属性,或者把数据赋值给子组件data里的一个变量
}
2.父子组件通信--->子向父{
// 父组件:
// 在template里定义事件
// 在methods里写函数,监听子组件的事件触发
// 子组件:
// 在数据变化后,用$emit触发即可
}
3.eventBus:{
import Vue from 'vue'
new Vue({
el: '#app',
components: { App },
template: '<App/>',
data(){
return {
Bus : new Vue()
}
}
})
// 通过在根组件,也就是#app组件上定义了一个所有组件都可以访问到的组件
// 给app组件添加Bus属性 (这样所有组件都可以通过this.$root.Bus访问到它,而且不需要引入任何文件)
// 2.在组件1里,this.$root.Bus.$emit触发事件
emitincrement(){
this.number++
this.$root.Bus.$emit('eventName', this.number)
},
// 3.在组件2里,this.$root.Bus.$on监听事件
mounted(){
this.$root.Bus.$on('eventName', value => {
this.number = value
console.log('busEvent');
})
}
}
4.利用localStorage或者sessionStorage
5. 利用Vuex:{
初始化VueX
{
// 新建一个store文件夹(这个不是必须的),并在文件夹下新建store.js文件,文件中引入我们的vue和vuex
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
//初始化数据
const state = {
count: 0
};
var vuexStore = new Vuex.Store({
state
});
// 使用 export default 封装,让外部可以访问
}
在需要使用的组件中引入store.js,并使用
{
// 这里有3中方法去得到VueX里面的值
1.// 使用this获取或通过computed的计算属性直接赋值
{
import store from "../store/store.js";
export default {
store,//使用
data(){
return{
//取值
count:this.$store.state.count
}
}
};
//或则
computed:{
count(){
return this.$store.state.count;
}
}
}
2.//通过mapState的对象来赋值
{
import {mapState} from 'vuex';
computed:mapState({
//理解为传入state对象,修改state.count属性
count:state=>state.count
})
}
3.//通过mapState的数组来赋值
{
computed:mapState(["count"])
}
}
创建改变状态的方法-->mutations
{
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
const state = {
count: 0
};
//改变状态的方法
const mutations = {
add(state) {
state.count++;
},
mul(state) {
state.count--;
}
};
var vuexStore = new Vuex.Store({
state,
mutations //引入
});
// 使用 export default 封装,让外部可以访问
}
使用改变状态的方法
{
// 在需要使用的组件中
// <button @click="$store.commit('add')">add</button>
}
使用getters的方式更新数据
{
const getters = {
count:function(state){
return state.count +=100;
}
};
var vuexStore = new Vuex.Store({
state,
mutations,
getters
});
// 组件中导入mapGetters,并使用
// 1.导入
// import { mapGetters } from 'vuex';
// 2.在methods中加入
// ...mapGetters(["count"])
// 3.在页面中使用
// <button @click="count()">mapGetters模式的点击事件</button>
}
actions异步修改状态
{
1.在store中创建方法
- context:上下文对象,这里你可以理解称store本身
- {commit}:直接把commit对象传递过来,可以让方法体逻辑和代码更清晰明了
const actions ={
addAction(context){
context.commit('add',10)
},
reduceAction({commit}){
commit('reduce')
}
}
2.组件中import导入mapActions
import { mapActions } from 'vuex';
3.methods中加入mapActions
...mapActions(['addAction','reduceAction'])
4.页面中使用
// <button @click="addAction">mapActions模式点击事件</button>
}
State
// State负责存储整个应用的状态数据,一般需要在使用的时候在跟节点注入store对象,后期就可以使用this.$store.state直接获取状态
{
//store为实例化生成的
import store from './store'
new Vue({
el: '#app',
store,
render: h => h(App)
})
// 这个store可以理解为一个容器,包含着应用中的state等。实例化生成store的过程是:
const mutations = {...};
const actions = {...};
const state = {...};
Vuex.Store({
state,
actions,
mutation
});
// 后续在组件中使用的过程中,如果想要获取对应的状态你就可以直接使用this.$store.state获取,
// 当然,也可以利用vuex提供的mapState辅助函数将state映射到计算属性中去,如
{
//我是组件
import {mapState} from 'vuex'
export default {
computed: mapState({
count: state => state.count
})
}
}
}
Mutations
//Mutations的中文意思是“变化”,利用它可以更改状态,本质就是用来处理数据的函数,
// 其接收唯一参数值state。store.commit(mutationName)是用来触发一个mutation的方法。
// 需要记住的是,定义的mutation必须是同步函数,否则devtool中的数据将可能出现问题,使状态改变变得难以跟踪。
{
const mutations = {
mutationName(state) {
//在这里改变state中的数据
}
}
// 在组件中触发:
//我是一个组件
export default {
methods: {
handleClick() {
this.$store.commit('mutationName')
}
}
}
// 或者使用辅助函数mapMutations直接将触发函数映射到methods上,这样就能在元素事件绑定上直接使用了。如:
import {mapMutations} from 'vuex'
//我是一个组件
export default {
methods: mapMutations([
'mutationName'
])
}
}
Actions
//Actions也可以用于改变状态,不过是通过触发mutation实现的,重要的是可以包含异步操作。
// 其辅助函数是mapActions与mapMutations类似,也是绑定在组件的methods上的。
// 如果选择直接触发的话,使用this.$store.dispatch(actionName)方法
{
//定义Actions
const actions = {
actionName({ commit }) {
//dosomething
commit('mutationName')
}
}
//在组件中使用
import {mapActions} from 'vuex'
//我是一个组件
export default {
methods: mapActions([
'actionName',
])
}
}
// Getters // 有些状态需要做二次处理,就可以使用getters。 // 通过this.$store.getters.valueName对派生出来的状态进行访问。 // 或者直接使用辅助函数mapGetters将其映射到本地计算属性中去。 { const getters = { strLength: state => state.aString.length } //上面的代码根据aString状态派生出了一个strLength状态 //在组件中使用 import {mapGetters} from 'vuex' //我是一个组件 export default { computed: mapGetters([ 'strLength' ]) } } }
}
动态组件(){
// 有的时候,在不同组件之间进行动态切换是非常有用的,比如在一个多标签的界面里:
// 上述内容可以通过 Vue 的 <component> 元素加一个特殊的 is 特性来实现:
// <component v-bind:is="currentTabComponent"></component>
// 在上述示例中,currentTabComponent 可以包括
// 已注册组件的名字,或
// 一个组件的选项对象
}
Vue组件注册可分为全局注册和局部注册
全局注册(){
需在初始化根实例之前注册组件
//html
<div id="example">
<my-component></my-component>
</div>
// 注册
Vue.component('my-component', {
template: '<div>A custom component!</div>'
})
// 创建根实例
new Vue({
el: '#example'
})
}
局部注册(){
// 通过Vue 实例/组件的实例选项 components 注册仅在其作用域中可用的组件。这种封装也适用于其它可注册的 Vue 功能,比如指令
var Child = {
template: '<div>A custom component!</div>'
}
new Vue({
// ...
components: {
// <my-component> 将只在父组件模板中可用
'my-component': Child
}
})
}
function 与react对比() {
// Vue中的props更灵活,对于class和Style特性,采用合并的策略,
// 并且需要在子组件中显示声明props,相同的地方是都有props验证,单项prop数据流
// props是可以动态变化的,子组件也实时更新,在react中官方建议props要像纯函数那样,输入输出一致对应,而且不太建议通过props来更改视图
// 子组件一般要显示地调用props选项来声明它期待获得的数据。而在react中不必需,另两者都有props校验机制
// 每个Vue实例都实现了事件接口,方便父子组件通信,小型项目中不需要引入状态管理机制,而react必需自己实现
// 多了指令系统,让模版可以实现更丰富的功能,而React只能使用JSX语法
// Vue增加的语法糖computed和watch,而在React中需要自己写一套逻辑来实现
}
function vue与JQ() {
// jQuery是使用选择器($)选取DOM对象,对其进行赋值、取值、事件绑定等操作,
// 其实和原生的HTML的区别只在于可以更方便的选取和操作DOM对象,而数据和界面是在一起的。
// 比如需要获取label标签的内容:$("lable").val();,它还是依赖DOM元素的值。
// Vue则是通过Vue对象将数据和View完全分离开来了。对数据进行操作不再需要引用相应的DOM对象,
// 可以说数据和View是分离的,他们通过Vue对象这个vm实现相互的绑定。这就是传说中的MVVM。
}
function MVVM() {
// 1. 低耦合。View可以独立于Model变化和修改,一个ViewModel可以绑定到不同的View上,
// 当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
// 2. 可重用性。可以把一些视图的逻辑放在ViewModel里面,让很多View重用这段视图逻辑。
// 3.独立开发。开发人员可以专注与业务逻辑和数据的开发(ViewModel)。
// 设计人员可以专注于界面(View)的设计。
// 4. 可测试性。可以针对ViewModel来对界面(View)进行测试
}
function 生命周期() {
// beforeCreate
组件实例刚被创建,组件属性计算之前,如data属性等
// created
组件实例创建完成,属性已绑定,但dom还未生成,$el属性还不存在
// beforeMount
模板编译/挂载之前
// mounted
模板编译/挂载之后
// beforeUpdate
组件更新之前
// updated
组件更新之后
// beforeDestroy
组件销毁前调用
// destroyed
组件销毁后调用
beforecreate : 举个栗子:可以在这加个loading事件
created :在这结束loading,还做一些初始化,实现函数自执行
mounted : 在这发起后端请求,拿回数据,配合路由钩子做一些事情
beforeDestroy: 你确认删除XX吗?
destroyed :当前组件已被删除,清空相关内容
}