Vue学习笔记
第1章:Vue入门
1.1 Vue简介
1.1.1 官网
- 英文官网: vuejs.org/
- 中文官网: cn.vuejs.org/
- Vue3.0 + TypeScript: 24kcs.github.io/vue3_study/
1.1.2 介绍描述
- 渐进式 (主张少)
JavaScript框架 - 作者: 尤雨溪(一位华裔前 Google 工程师)
- 作用: 动态构建用户界面
1.1.3 Vue的特点
- 遵循
MVVM模式 - 编码简洁, 体积小, 运行效率高, 适合移动/PC 端开发
- 它本身只关注 UI, 可以轻松引入 vue 插件或其它第三库开发项目
1.1.4 与其它前端 JS 框架的关联
- 借鉴
angular的模板和数据绑定技术 - 借鉴
react的组件化和虚拟 DOM技术
1.1.5. Vue 扩展插件
- vue-cli: vue 脚手架
- vue-resource(axios): ajax 请求
- vue-router: 路由
- vuex: 状态管理
- vue-lazyload: 图片懒加载
- vue-scroller: 页面滑动相关
- mint-ui: 基于 vue 的 UI 组件库(移动端)
- element-ui: 基于 vue 的 UI 组件库(PC 端)
1.2 Vue的基本使用
1.2.1 编码
<template>
<div id="app">
<Main/>
</div>
</template>
<script>
import Header from '@/components/Layout/Header/Header';
import Main from '@/components/Layout/Main/Main';
import Navbar from '@/components/NavBar/NavBar';
import { mapState, mapActions } from 'vuex';
export default {
name: 'App',
components: {
Main
},
data() {
return {
route: this.$route
};
},
computed: {
...mapState({
isH5Page: state => state.setting.isH5Page,
activeNameText: state => state.setting.activeNameText
})
},
created() {
},
mounted() {
const screenWidth = document.body.clientWidth;
screenWidth >= 800 ? this.setPageAction(false) : this.setPageAction(true);
},
methods: {
...mapActions(['setPageAction', 'setActiveNameText'])
}
};
</script>
<style lang="scss">
#app {
height: auto;
min-width: 300px;
background-color: #fff;
}
</style>
1.2.1. 理解 Vue 的 MVVM
1.3 模板语法
1.3.1 模板的理解
- 动态的 html 页面
- 包含了一些 JS 语法代码 a. 双大括号表达式 b. 指令(以 v-开头的自定义标签属性)
1.3.2 双大括号表达式
-
语法: {{exp}}
-
功能: 向页面输出数据
-
可以调用对象的方法
<span>{{ lang.mustChangePwd }}</span>
1.3.3 指令一: 强制数据绑定
- 功能: 指定变化的属性值
- 完整写法: v-bind:xxx='yyy' //yyy 会作为表达式解析执行
- 简洁写法: :xxx='yyy'
<el-tag
v-for="(item, i) in data1" v-if="show > i"
:class="{wrap:wrap}" :key="item"
:style="{'font-size':fontSize || '13px'}"
>
{{getLabel(item)}}
</el-tag>
1.3.4 指令二: 绑定事件监听
- 功能: 绑定指定事件名的回调函数
- 完整写法: v-on:keyup='xxx' v-on:keyup='xxx(参数)' v-on:keyup.enter='xxx'
- 简洁写法: @keyup='xxx' @keyup.enter='xxx'
<div @click="toLogin">
<i class="iconfont iconcuowu"></i>
</div>
1.4 计算属性和监视
1.4.1 computed 计算属性
- 在 computed 属性对象中定义计算属性的方法
- 在页面中使用{{方法名}}来显示计算的结果
常用来获取vuex里的状态
computed: {
...mapState({
userInfo: state => state.user.userInfo,
isH5Page: state => state.setting.isH5Page
}),
...mapGetters(["language"])
},
1.4.3. watch 监视属性
- 通过通过 vm 对象的$watch()或 watch 配置来监视指定的属性
- 当属性变化时, 回调函数自动调用, 在函数内部进行计算
watch: {
language() {
this.lang = {...this.$t("loginInfo")};
const text = this.lang.fromTotime;
this.search = [
{
label: text,
key: "loginTime",
type: "date"
}
];
},
userInfo: {
handler(v) {
this.getIcon();
this.getUserName();
},
deep: true
}
},
1.4.4. 计算属性高级
- 通过 getter/setter 实现对属性数据的显示和监视
- 计算属性存在缓存, 多次读取只执行一次 getter 计算
在这里插入代码片
1.5. class 与 style 绑定
1.5.1 理解
- 在应用界面中, 某个(些)元素的样式是变化的
- class/style 绑定就是专门用来实现动态样式效果的技术
1.5.2 class 绑定
- :class='xxx'
- 表达式是字符串: 'classA'
- 表达式是对象: {classA:isA, classB: isB}
- 表达式是数组: ['classA', 'classB']
1.5.3 style 绑定
- :style="{ color: activeColor, fontSize: fontSize + 'px' }"
- 其中 activeColor/fontSize 是 data 属性
1.6 条件渲染
1.6.1 条件渲染指令
- v-if 与 v-else
- v-show
1.6.2 比较 v-if 与 v-show
- 如果需要频繁切换 v-show 较好
- 当条件不成立时, v-if 的所有子节点不会解析(项目中使用)
1.7. 列表渲染
- 列表显示指令 数组: v-for / index 对象: v-for / key
- 列表的更新显示 删除 item 替换 item
- 列表的高级处理 列表过滤 列表排序
<template v-for="(val,index) in data">
<div :key="index" class="in-form" v-if="!val.hasOwnProperty('isShow') || val.isShow">
</div>
</template>
1.8 事件处理
1.8.2. 绑定监听:
- v-on:xxx="fun"
- @xxx="fun"
- @xxx="fun(参数)"
- 默认事件形参: event
- 隐含属性对象: $event
1.8.3 事件修饰符
- .prevent : 阻止事件的默认行为 event.preventDefault()
- .stop : 停止事件冒泡 event.stopPropagation()
1.8.4. 按键修饰符
- .keycode : 操作的是某个 keycode 值的键
- .keyName : 操作的某个按键名的键(少部分)
1.9. 表单输入绑定
1.9.1 使用 v-model 对表单数据自动收集
- text/textarea
- checkbox
- radio
- select
1.10. Vue 实例生命周期
1.10.1 生命周期流程图
1.10.3. vue 生命周期分析
- 初始化显示
- beforeCreate()
- created()
- beforeMount()
- mounted()
- 更新状态: this.xxx = value
- beforeUpdate()
- updated()
- 销毁 vue 实例: vm.$destory()
- beforeDestory()
- destoryed()
1.10.4. 常用的生命周期方法
- created()/mounted(): 发送 ajax 请求, 启动定时器等异步任务
- beforeDestory(): 做收尾工作, 如: 清除定时器
1.11. 过渡&动画
1.11.2. vue 动画的理解
- 操作 css 的 trasition 或 animation
- vue 会给目标元素添加/移除特定的 class
- 过渡的相关类名 xxx-enter-active: 指定显示的 transition xxx-leave-active: 指定隐藏的 transition xxx-enter/xxx-leave-to: 指定隐藏时的样式
1.11.3. 基本过渡动画的编码
- 在目标元素外包裹
- 定义 class 样式 指定过渡样式: transition 指定隐藏时的样式: opacity/其它
1.12. 过滤器
1.12.2. 理解过滤器
- 功能: 对要显示的数据进行特定格式化后再显示
- 注意: 并没有改变原本的数据, 可是产生新的对应的数据
1.12.3. 定义和使用过滤器
- 定义过滤器
Vue.filter(filterName, function(value[,arg1,arg2,...]){ // 进行一定的数据处理 return newValue }) - 使用过滤器
<div>{{myData | filterName}}</div> <div>{{myData | filterName(arg)}}</div>
1.13. 内置指令与自定义指令
1.13.2. 常用内置指令
- v:text : 更新元素的 textContent
- v-html : 更新元素的 innerHTML
- v-if : 如果为 true, 当前标签才会输出到页面
- v-else: 如果为 false, 当前标签才会输出到页面
- v-show : 通过控制 display 样式来控制显示/隐藏
- v-for : 遍历数组/对象
- v-on : 绑定事件监听, 一般简写为@
- v-bind : 强制绑定解析表达式, 可以省略 v-bind
- v-model : 双向数据绑定
- ref : 指定唯一标识, vue 对象通过$refs 属性访问这个元素对象
- v-cloak : 防止闪现, 与 css 配合: [v-cloak] { display: none }
1.13.3. 自定义指令
- 注册全局指令
Vue.directive('my-directive', function(el, binding){ el.innerHTML = binding.value.toupperCase() }) - 注册局部指令
directives : { 'my-directive' : { bind (el, binding) { el.innerHTML = binding.value.toupperCase() } } } - 使用指令
v-my-directive='xxx'
第 2 章:vue 组件化编码
2.1. 使用 vue-cli 创建模板项目
2.1.1. 说明
- vue-cli 是 vue 官方提供的脚手架工具
- github: github.com/vuejs/vue-c…
- 作用: 从 github.com/vuejs-templ… 下载模板项目
2.1.2. 创建 vue 项目
npm install -g
vue-cli vue init webpack vue_demo
cd vue_demo
npm install
npm run dev
访问: http://localhost:8080/
2.1.3. 模板项目的结构
- build : webpack 相关的配置文件夹(基本不需要修改)
- dev-server.js : 通过 express 启动后台服务器
- config: webpack 相关的配置文件夹(基本不需要修改)
- index.js: 指定的后台服务的端口号和静态资源文件夹
- node_modules 依赖包
- src : 源码文件夹
- components: vue 组件及其相关资源文件夹
- App.vue: 应用根主组件
- main.js: 应用入口 js
- static: 静态资源文件夹
- .babelrc: babel 的配置文件
- .eslintignore: eslint 检查忽略的配置
- .eslintrc.js: eslint 检查的配置
- .gitignore: git 版本管制忽略的配置
- index.html: 主页面文件
- package.json: 应用包配置文件
- README.md: 应用描述说明的 readme 文件
2.2. 项目的打包与发布
2.2.1. 打包:
npm run build
2.2.2. 发布 1: 使用静态服务器工具包
npm install -g serve serve dist 访问: http://localhost:5000
2.2.3. 发布 2: 使用动态 web 服务器(tomcat)
修改配置: webpack.prod.conf.js
output: { publicPath: '/xxx/' //打包文件夹的名称 }
重新打包:
npm run build
修改 dist 文件夹为项目名称: xxx 将 xxx 拷贝到运行的 tomcat 的 webapps 目录下 访问: http://localhost:8080/xxx
2.3. eslint
2.3.1. 说明
- ESLint 是一个代码规范检查工具
- 它定义了很多特定的规则, 一旦你的代码违背了某一规则, eslint会作出非常有用的提示
- 官网: eslint.org/
- 基本已替代以前的 JSLint
2.3.2. ESLint 提供以下支持
- ES
- JSX
- style 检查
- 自定义错误和提示
2.3.3. ESLint 提供以下几种校验
- 语法错误校验
- 不重要或丢失的标点符号,如分号
- 没法运行到的代码块(使用过 WebStorm 的童鞋应该了解)
- 未被使用的参数提醒
- 确保样式的统一规则,如 sass 或者 less
- 检查变量的命名
2.3.5. 相关配置文件
- .eslintrc.js : 全局规则配置文件
'rules': { 'no-new': 1 } - 在 js/vue 文件中修改局部规则
/* eslint-disable no-new */ new Vue({ el: 'body', components: { App } }) - .eslintignore: 指令检查忽略的文件 *.js *.vue
2.4. 组件定义与使用
2.4.1. vue 文件的组成(3 个部分)
- 模板页面
<template> 页面模板 </template> - JS 模块对象
<script> export default { data() {return {}}, methods: {}, computed: {}, components: {} } </script> - 样式
<style>样式定义 </style>
2.4.2. 基本使用
-
引入组件
-
映射成标签
-
使用组件标签
<template> <HelloWorld></HelloWorld> <hello-world></hello-world> </template> <script> import HelloWorld from './components/HelloWorld' export default { components: { HelloWorld } } </script>
2.4.3. 关于标签名与标签属性名书写问题
- 写法一: 一模一样
- 写法二: 大写变小写, 并用-连接
2.5. 组件间通信
2.5.1. 组件间通信基本原则
- 不要在子组件中直接修改父组件的状态数据
- 数据在哪, 更新数据的行为(函数)就应该定义在哪
2.5.2. vue 组件间通信方式
- props
- vue 的自定义事件
- 消息订阅与发布(如: pubsub 库)
- slot
- vuex(后面单独讲)
2.6. 组件间通信 1: props
2.6.1. 使用组件标签时
<my-component name='tom' :age='3' :set-name='setName'></my-component>
2.6.2. 定义 MyComponent 时
-
在组件内声明所有的 props
-
方式一: 只指定名称
props: ['name', 'age', 'setName'] -
方式二: 指定名称和类型
props: { name: String, age: Number, setNmae: Function } -
方式三: 指定名称/类型/必要性/默认值
props: { name: {type: String, required: true, default:xxx}, }
2.6.3. 注意
- 此方式用于父组件向子组件传递数据
- 所有标签属性都会成为组件对象的属性, 模板页面可以直接引用
- 问题: a. 如果需要向非子后代传递数据必须多层逐层传递 b. 兄弟组件间也不能直接 props 通信, 必须借助父组件才可以
2.7. 组件间通信 2: vue 自定义事件
2.7.1. 绑定事件监听
// 方式一: 通过 v-on 绑定
@delete_todo="deleteTodo"
// 方式二: 通过$on()
this.$refs.xxx.$on('delete_todo', function (todo) {
this.deleteTodo(todo)
})
2.7.2. 触发事件
// 触发事件(只能在父组件中接收)
this.$emit(eventName, data)
2.7.3. 注意:
-
此方式只用于子组件向父组件发送消息(数据)
-
问题: 隔代组件或兄弟组件间通信此种方式不合适
2.8. 组件间通信 3: 消息订阅与发布(PubSubJS 库)
2.8.1. 订阅消息
PubSub.subscribe('msg', function(msg, data){})
2.8.2. 发布消息
PubSub.publish('msg', data)
2.8.3. 注意
- 优点: 此方式可实现任意关系组件间通信(数据)
2.8.4. 事件的 2 个重要操作(总结)
-
绑定事件监听 (订阅消息)
目标: 标签元素 事件名(类型): click/focus 回调函数: function(event){}
-
触发事件 (发布消息) DOM 事件: 用户在浏览器上对应的界面上做对应的操作 自定义: 编码手动触发
2.9. 组件间通信 4: slot
2.9.1. 理解
此方式用于父组件向子组件传递标签数据
2.9.2. 子组件: Child.vue
<template>
<div>
<slot name="xxx">不确定的标签结构 1</slot>
<div>组件确定的标签结构</div>
<slot name="yyy">不确定的标签结构 2</slot>
</div>
</template>
2.9.3. 父组件: Parent.vue
<child>
<div slot="xxx">xxx 对应的标签结构</div>
<div slot="yyy">yyyy 对应的标签结构</div>
</child>
第 3 章:vue-ajax
3.1. vue 项目中常用的 2 个 ajax 库
3.1.1. vue-resource
vue 插件, 非官方库, vue1.x 使用广泛
3.1.2. axios
通用的 ajax 请求库, 官方推荐, vue2.x 使用广泛
3.2. vue-resource 的使用
3.2.1. 在线文档
3.2.2. 下载
npm install vue-resource --save
3.2.3. 编码
// 引入模块
import VueResource from 'vue-resource'
// 使用插件
Vue.use(VueResource)
// 通过 vue/组件对象发送 ajax 请求 this.$http.get('/someUrl').then((response) => {
// success callback
console.log(response.data) //返回结果数据
}, (response) => {
// error callback
console.log(response.statusText) //错误信息
})
3.3. axios 的使用
3.3.2. 在线文档
3.3.3. 在线文档
npm install axios --save
3.3.4. 编码
// 引入模块
import axios from 'axios'
// 发送 ajax 请求
axios.get(url)
.then(response => {
console.log(response.data) // 得到返回结果数据
})
.catch(error => {
console.log(error.message)
})
第 4 章:vue UI 组件库
4.1. 常用
- Mint UI: a. 主页: mint-ui.github.io/#!/zh-cn b. 说明: 饿了么开源的基于 vue 的移动端 UI 组件库
- Elment a. 主页: element-cn.eleme.io/#/zh-CN b. 说明: 饿了么开源的基于 vue 的 PC 端 UI 组件库
4.2. 使用 Mint UI
4.2.1. 下载:
npm install --save mint-ui
4.2.2. 实现按需打包
1. 下载
npm install --save-dev babel-plugin-component
2. 修改 babel 配置
"plugins": ["transform-runtime",["component", [
{
"libraryName": "mint-ui",
"style": true
}
]]]
4.2.3. mint-ui 组件分类
- 标签组件
- 非标签组件
4.2.4. 使用 mint-ui 的组件
-
main.js
import {Button} from 'mint-ui' Vue.component(Button.name, Button) -
App.vue
<template> <mt-button @click="handleClick" type="primary" style="width: 100%">Test</mt-button> </template> <script> import {Toast} from 'mint-ui' export default { methods: { handleClick () { Toast('点击了测试'); } } } </script>
第 5 章:vue-router
5.1. 理解
5.1.1. 说明
- 官方提供的用来实现 SPA 的 vue 插件
- github: github.com/vuejs/vue-r…
- 中文文档: router.vuejs.org/zh-cn/
- 下载: npm install vue-router --save
5.1.2. 相关 API 说明
-
VueRouter(): 用于创建路由器的构建函数
new VueRouter({ // 多个配置项 }) -
路由配置
routes: [ { // 一般路由 path: '/about', component: About }, { // 自动跳转路由 path: '/', redirect: '/about' } ] -
注册路由器
import router from './router' new Vue({ router }) -
使用路由组件标签
1. <router-link>: 用来生成路由链接 <router-link to="/xxx">Go to XXX</router-link> 2. <router-view>: 用来显示当前路由组件界面 <router-view></router-view>
5.2. 基本路由
5.2.2. 路由组件
Home.vue About.vue
5.2.3. 应用组件: App.vue
<div>
<!--路由链接-->
<router-link to="/about">About</router-link>
<router-link to="/home">Home</router-link>
<!--用于渲染当前路由组件-->
<router-view></router-view>
</div>
5.2.4. 路由器模块: src/router/index.js
export default new VueRouter({
routes: [
{
path: '/',
redirect: '/about'
},
{
path: '/about',
component: About
},
{
path: '/home',
component: Home
}
]
})
5.2.5. 注册路由器: main.js
import Vue from 'vue'
import router from './router'
// 创建 vue 配置路由器
new Vue({
el: '#app',
router,
render: h => h(app)
})
5.2.6. 优化路由器配置
linkActiveClass: 'active', // 指定选中的路由链接的 class
5.2.7. 总结: 编写使用路由的 3 步
- 定义路由组件
- 注册路由
- 使用路由
<router-link><router-view>
5.3. 嵌套路由
5.3.2. 子路由组件
News.vue Message.vue
5.3.3. 配置嵌套路由: router.js
path: '/home',
component: home,
children: [
{
path: 'news',
component: News
},
{
path: 'message',
component: Message
}
]
5.3.4. 路由链接: Home.vue
<router-link to="/home/news">News</router-link>
<router-link to="/home/message">Message</router-link>
<router-view></route-view>
5.4. 向路由组件传递数据
5.4.2. 方式 1: 路由路径携带参数(param/query)
-
配置路由
children: [ { path: 'mdetail/:id', component: MessageDetail } ] -
路由路径
<router-link :to="'/home/message/mdetail/'+m.id">{{m.title}}</router-link> -
路由组件中读取请求参数
this.$route.params.id
5.4.3. 方式 2: 属性携带数据
<router-view :msg="msg"></router-view>
5.5. 缓存路由组件对象
5.5.1. 理解
- 默认情况下, 被切换的路由组件对象会死亡释放, 再次回来时是重新创建的
- 如果可以缓存路由组件对象, 可以提高用户体验
5.5.2. 编码实现
<keep-alive>
<router-view></router-view>
</keep-alive>
5.6.2. 相关 API
- this.$router.push(path): 相当于点击路由链接(可以返回到当前路由界面)
- this.$router.replace(path): 用新路由替换当前路由(不可以返回到当前路由界面)
- this.$router.back(): 请求(返回)上一个记录路由
- this.router.go(1): 请求下一个记录路由
第 6 章:vuex
6.1. vuex 理解
6.1.1. vuex 是什么
- github 站点: github.com/vuejs/vuex
- 在线文档: vuex.vuejs.org/zh-cn/
- 简单来说: 对 vue 应用中多个组件的共享状态进行集中式的管理(读/写)
6.1.2. 状态自管理应用
- state: 驱动应用的数据源
- view: 以声明方式将 state 映射到视图
- actions: 响应在 view 上的用户输入导致的状态变化(包含 n 个更新状态的方法)
6.1.3. 多组件共享状态的问题
- 多个视图依赖于同一状态
- 来自不同视图的行为需要变更同一状态
- 以前的解决办法 a. 将数据以及操作数据的行为都定义在父组件 b. 将数据以及操作数据的行为传递给需要的各个子组件(有可能需要多级传递)
- vuex 就是用来解决这个问题的
6.2. vuex 核心概念和 API
6.2.1. state
-
vuex 管理的状态对象
-
它应该是唯一的
const state = { xxx: initValue }
6.2.2. mutations
- 包含多个直接更新 state 的方法(回调函数)的对象
- 谁来触发: action 中的 commit('mutation 名称')
- 只能包含同步的代码, 不能写异步代码
const mutations = {
yyy (state, {data1}) {
// 更新 state 的某个属性
}
}
6.2.3. actions
- 包含多个事件回调函数的对象
- 通过执行:
commit()来触发mutation的调用, 间接更新state - 谁来触发: 组件中:
$store.dispatch('action 名称', data1) // 'zzz' - 可以包含异步代码(定时器, ajax)
const actions = {
zzz ({commit, state}, data1) {
commit('yyy', {data1})
}
}
6.2.4. getters
- 包含多个计算属性(get)的对象
- 谁来读取: 组件中:
$store.getters.xxx
const getters = {
mmm (state) {
return ...
}
}
6.2.5. modules
- 包含多个 module
- 一个 module 是一个 store 的配置对象
- 与一个组件(包含有共享数据)对应
6.2.6. 向外暴露 store 对象
export default new Vuex.Store({
state,
mutations,
actions,
getters
})
6.2.7. 组件中
import {mapState, mapGetters, mapActions} from 'vuex'
export default {
computed: {
...mapState(['xxx']),
...mapGetters(['mmm']), }
methods: mapActions(['zzz']) }
{{xxx}} {{mmm}} @click="zzz(data)"
6.2.8. 映射 store
import store from './store'
new Vue({ store })
6.2.9. store 对象
- 所有用 vuex 管理的组件中都多了一个属性$store, 它就是一个 store 对象
- 属性: state: 注册的 state 对象 getters: 注册的 getters 对象
- 方法: dispatch(actionName, data): 分发调用 action
6.5. vuex 结构分析
第 7 章:vue 源码分析
7.1. 说明
- 分析 vue 作为一个 MVVM 框架的基本实现原理 数据代理 模板解析 数据绑定
- 剖析 github 上某基友仿 vue 实现的 mvvm 库
- 地址: github.com/DMQ/mvvm
7.2. 准备知识
- [].slice.call(lis): 将伪数组转换为真数组
- node.nodeType: 得到节点类型
- Object.defineProperty(obj, propName, {}): 给对象添加/修改属性(指定描述符) configurable: true/false 是否可以重新 define enumerable: true/false 是否可以枚举(for..in / keys()) value: 指定初始值 writable: true/false value 是否可以修改 get: 回调函数, 用来得到当前属性值 set: 回调函数, 用来监视当前属性值的变化
- Object.keys(obj): 得到对象自身可枚举的属性名的数组
- DocumentFragment: 文档碎片(高效批量更新多个节点)
- obj.hasOwnProperty(prop): 判断 prop 是否是 obj 自身的属性
7.3. 数据代理
- 数据代理: 通过一个对象代理对另一个对象(在前一个对象内部)中属性的操作(读/写)
- vue 数据代理: 通过 vm 对象来代理 data 对象中所有属性的操作
- 好处: 更方便的操作 data 中的数据
- 基本实现流程 a. 通过 Object.defineProperty()给 vm 添加与 data 对象的属性对应的属性描述符 b. 所有添加的属性都包含 getter/setter c. getter/setter 内部去操作 data 中对应的属性数据
7.4. 模板解析
7.4.1. 模板解析的基本流程
- 将 el 的所有子节点取出, 添加到一个新建的文档 fragment 对象中
- 对 fragment 中的所有层次子节点递归进行编译解析处理
- 对大括号表达式文本节点进行解析
- 对元素节点的指令属性进行解析
- 事件指令解析
- 一般指令解析
- 将解析后的 fragment 添加到 el 中显示
7.4.2. 模板解析(1): 大括号表达式解析
- 根据正则对象得到匹配出的表达式字符串: 子匹配/RegExp.$1 name
- 从 data 中取出表达式对应的属性值
- 将属性值设置为文本节点的 textContent
7.4.3. 模板解析(2): 事件指令解析
- 从指令名中取出事件名
- 根据指令的值(表达式)从 methods 中得到对应的事件处理函数对象
- 给当前元素节点绑定指定事件名和回调函数的 dom 事件监听
- 指令解析完后, 移除此指令属性
7.4.4. 模板解析(3): 一般指令解析
- 得到指令名和指令值(表达式) text/html/class msg/myClass
- 从 data 中根据表达式得到对应的值
- 根据指令名确定需要操作元素节点的什么属性
- v-text---textContent 属性
- v-html---innerHTML 属性
- v-class--className 属性
- 将得到的表达式的值设置到对应的属性上
- 移除元素的指令属性
7.5. 数据绑定
7.5.1. 数据绑定
==一旦更新了 data 中的某个属性数据, 所有界面上直接使用或间接使用了此属性的节点都会 更新==
7.5.2. 数据劫持
- 数据劫持是 vue 中用来实现数据绑定的一种技术
- 基本思想: 通过 defineProperty()来监视 data 中所有属性(任意层次)数据的变化, 一旦变 化就去更新界面
7.5.3. 四个重要对象
7.5.3.1 Observer
a. 用来对 data 所有属性数据进行劫持的构造函数 b. 给 data 中所有属性重新定义属性描述(get/set) c. 为 data 中的每个属性创建对应的 dep 对象
7.5.3.2 Dep(Depend)
a. data 中的每个属性(所有层次)都对应一个 dep 对象 b. 创建的时机:
- 在初始化 define data 中各个属性时创建对应的 dep 对象
- 在 data 中的某个属性值被设置为新的对象时
c. 对象的结构
{
id, // 每个 dep 都有一个唯一的 id
subs //包含 n 个对应 watcher 的数组(subscribes 的简写)
}
d. subs 属性说明
- 当 watcher 被创建时, 内部将当前 watcher 对象添加到对应的 dep 对象的 subs 中
- 当此 data 属性的值发生改变时, subs 中所有的 watcher 都会收到更新的通知,从而最终更新对应的界面
7.5.3.3 Compiler
a. 用来解析模板页面的对象的构造函数(一个实例) b. 利用 compile 对象解析模板页面 c. 每解析一个表达式(非事件指令)都会创建一个对应的 watcher 对象, 并建立 watcher 与 dep 的关系 d. complie 与 watcher 关系: 一对多的关系
7.5.3.4 Watcher
a. 模板中每个非事件指令或表达式都对应一个 watcher 对象 b. 监视当前表达式数据的变化 c. 创建的时机: 在初始化编译模板时 d. 对象的组成
{
vm, //vm 对象
exp, //对应指令的表达式
cb, //当表达式所对应的数据发生改变的回调函数
value, //表达式当前的值
depIds //表达式中各级属性所对应的 dep 对象的集合对象 //属性名为 dep 的 id, 属性值为 dep
}
7.5.3.5 总结: dep 与 watcher 的关系: 多对多
a. data 中的一个属性对应一个 dep, 一个 dep 中可能包含多个 watcher(模板中有几个 表达式使用到了同一个属性) b. 模板中一个非事件表达式对应一个 watcher, 一个 watcher 中可能包含多个 dep(表 达式是多层: a.b) c. 数据绑定使用到 2 个核心技术
- defineProperty()
- 消息订阅与发布
7.6. MVVM 原理图分析
7.7. 双向数据绑定
- 双向数据绑定是建立在单向数据绑定(model==>View)的基础之上的
- 双向数据绑定的实现流程: a. 在解析 v-model 指令时, 给当前元素添加 input 监听 b. 当 input 的 value 发生改变时, 将最新的值赋值给当前表达式所对应的 data 属性