什么是 Vue?
目录
12. nextTick、$nextTick、计算属性的缓存
Vue (发音为 /vjuː/,类似 view) 是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建,并提供了一套声明式的、组件化的编程模型,帮助你高效地开发用户界面。无论是简单还是复杂的界面,Vue 都可以胜任。
Vue.js(1.0)
1. $data、$el
<div id="app">{{message}}</div> <script type="text/javascript"> let vm = new Vue({ el : '#app', data : {message : '小花'} }); // $data : 表示data对象里的所有属性 vm.$data = {message : '小白'}; // $el : Vue实例的挂在元素 // 输出 <div id="app">小白</div> vm.$el; </script>
2. $watch
<div id="app">{{message}}</div> <script type="text/javascript" let vm = new Vue({ el : '#app', data : { message : {name:'小花'} } }); /** * $watch(表达式, callback(新参数, 旧参数), [options]) 监听Vue实例的一个表达 * $watch 返回一个取消函数 用来停止触发回调 * deep : true 普通监听不能监听到对象某一个参数值的变化 (数组变动不需要添加该参数) * 注 : 在修改(不是替换)对象或数组时 旧值将与新值相同 因为它们索引同一个对象/数组 Vue不会保留修改之前值的副本 */ let unwatch = vm.$watch( 'message', // callback(新值, 旧值) (newVal, oldVal) => c('name =', newVal.name, oldVal.name), // deep : true 监听对象的属性变化 {deep : true} ); // 如果调用$watch的回调则$watch不再监听变化 // unwatch(); // $data : 表示data对象里的所有属性 // $watch监听对象的变化 如果是替换 则记录旧值 输出 name = 小蓝 小花 vm.$data.message = {name:'小蓝'}; // $watch监听对象的变化 如果是修改 则不记录旧值 输出 name = 小蓝 小蓝 vm.$data.message.name = '小蓝'; </script>
3. Vue 生命周期
<div id="app">{{message}}</div> <script type="text/template"> let vm = new Vue({ el : '#app', data : {message : '小花'}, // 实例创建之后同步调用 // 已经建立 数据绑定 计算属性 方法 watcher/事件回调 // 但还没有开始DOM编译 $el还不存在 created (){ // 输出 小花 c('created : ', this.message); // 因为此时还没有开始DOM编译 所以$el还不存在 输出undefinde c('created : ', this.$el); }, // 在编译结束和 $el第一次插入文档之后调用 ready (){ c('ready : ', this.message); c('ready : ', this.$el); } }); </script>
4. partial
<partial name="my-hello" id="app"></partial> <!-- 输出 : 这里的变量不会被解析{{message}}, partial的可以 --> <div>{{{htmlText}}}</div> <!-- *只执行一次渲染 --> <div>{{*one}}</div> <script type="text/javascript"> // 注册partial 里面的变量可以被解析 // HTML引用 <partial name="my-hello" id="app"></partial> Vue.partial('my-hello', '<p>partial {{message}}</p>'); let vm = new Vue({ el: 'body', data: { message: '小蓝', htmlText: '这里的变量不会被解析{{message}}, partial的可以', one: '只执行一次插入值', }, ready() { // one的渲染节点 {{*one}} 只渲染一次 不再改变值 setTimeout(() => this.one = "改变值", 1000); } }); </script>
5. computed 计算属性
// 例-1 <div id="example">{{a}}-{{b}}</div> <script> let vm = new Vue({ el: '#example', data: { a: 1 }, // 计算属性需要添加computed // 计算属性不管变量值是否改变 只要页面引用了变量b // 或者页面里调用了变量b就会执行b方法 // computed 和 watch的区别是 watch必须改变值才会触发 computed: { b() { c('b'); return this.a + 1; } } }); </script> // 例-2 <div id="example">{{firstName}}-{{lastName}}-{{fullName}}</div> <script> let vm = new Vue({ el: '#example', data: { firstName: 'Foo', lastName: 'Bar' }, // 计算属性需要添加computed b的值也会影响a的值 // 计算属性不管变量值是否改变 只要页面引用了变量b 或者页面里调用了变量b就会执行b方法 // computed和watch的区别是 watch只有变量改变了才会触发 computed: { fullName: { // 计算属性默认只有get 当页面引用变量或者js里调用变量会执行 get() { return this.firstName + ' ' + this.lastName; }, // 设置了set 可以监听fullName变量 变化后就调用set set(newValue) { this.firstName = 'firstName我改变了'; this.lastName = 'lastName我改变了'; } } } }); // 2秒后改变 fullName的值 触发set // 重新设置firstName lastName setTimeout(() => vm.fullName = '小花', 2000); </script>
6. style 绑定
// 输出 <div class="static class-a"></div> 不会覆盖原有的class // class="{{className}}" 这么绑定会覆盖自有的class <div class="static" v-bind:class="{'class-a':isA, 'class-b':isB}"></div> // 输出 <div class="class-2 class1"></div> 不会覆盖原有的class <div v-bind:class="classObject" class="class-2"></div> // 输出 <div class="class-a class-b"></div> <div v-bind:class="[classA, classB]"></div> <script> // 给元素绑定class let vm = new Vue({ el : 'body', data : { // 对象语法 v-bind:class="{ 'class-a': isA, 'class-b': isB } isA : true, isB : false, // 绑定一个对象 v-bind:class="classObject" classObject : { 'class1' : true, 'class2' : false }, // [classA, classB] 数组语法 // 这里也可以只绑定一个对象 classA : 'class-a', classB : 'class-b' } }); </script>
7. v-if、v-show、v-else
/* v-show因为v-if每次切换都要局部编译/卸载 所以多次切换用v-show最好 */ // v-if v-else的标签只有显示时才在浏览器里渲染 否则不出现在DOM里 <h1 v-if="ok">yes</h1> <h1 v-else>No</h1> // v-if v-else 可以作用于template标签 输出一组数据 <template v-if="ok"> <h1>Title</h1> <p>Paragraph 1</p> <p>Paragraph 2</p> </template> // v-show 不论是否显示都在页面加载进行渲染 // v-show 不支持template渲染一组元素 <div v-show="ok">hello</div> <div v-else>Par</div> <script> let vm = new Vue({ el : 'body', data : { ok : false } }); </script>
8. v-for
// $index 数组对象里表示循环到第几个数据 // i等价$index <ul id="example-1"> <li v-for="(i, item) of items">{{item.message}} - {{$index}} - {{i}}</li> </ul> // template输出一组内容 <ul> <template v-for="(i, item) of items"> <li>{{item.message}}</li> <li class="divider">7</li> </template> </ul> // 如果数据是一个Object $key代表键值 // key等价于$key <ul id="repeat-object" class="demo"> <li v-for="(key, value) in object">{{value}} : {{$key}}</li> </ul> <script> // 给元素绑定class let vm = new Vue({ el: 'body', data: { parentMessage: 'Parent', items: [ { message: 'Foo' }, { message: 'Bar' } ], object: { FirstName: 'John', LastName: 'Doe', Age: 30 } } }); /* * 变异方法 : push()、pop()、shift()、unshift()、splice()、sort()、reverse() * vue封装了这些方法的变异方法 修改后 video会随之变化 * 这些方法会修改原始数组 */ /* * 替换数组 : filter()、concat()、slice() 不会修改原始数组 * 返回一个新数组 需要用新数组替换原始数组 */ vm.items.push({ message: 'Wang' }); </script>
9. 方法与事件处理器
// 不需要传参时 不用加 () <div v-on:click="greet">Greet</div> /* ** 事件修饰符 * .stop : 阻止冒泡 * .prevent : 阻止默认事件 * .stop.prevent : 可以串联 */ <a href="http://www.baidu.com" v-on:click.prevent="doThis">链接</a> /* ** 按键修饰符 * .enter : 回车 * .数值 : 具体某个按键 * .enter.left : 可以串联 */ <input type="text" v-on:keyup.13="submit"> <script> // 给元素绑定class let vm = new Vue({ el: 'body', data: { name: '测试数据' }, methods: { greet() { c(this.name); }, doThis() { c('跳转被阻止了'); }, submit() { c('只有当keyCode是13时才触发submit()'); } } }); </script>
10. v-model、v-bind
<span>Message is: {{ message }}</span> <input type="text" v-model="message" placeholder="edit me"> <script> // 给元素绑定class let vm = new Vue({ el: 'body', data: { // v-model 需要定义 否则第一次点击报错 message: '' } }); </script>
11. 组件、props、slot
/** * Vue.js * 子组件建议DropdownMenu || dropdownMenu 引用的时候 <dropdown-menu> * 建议给组件一个根节点 保证组件元素上的指令和特性能正确的转换 性能也会更好 */ <div id="example"> <my-component></my-component> </div> <script> // 注册全局组件 方法一. // 创建一个组件构造器 let MyComponent = Vue.extend({ // 组件里的data对象 必须以函数形式返回 // 为了避免所有实例共享一个data 可以互相修改 data() { return { info: 'relative' } }, template: '<p>测试数据</p>', // replace : // false 不替换挂在标签 // true 替换挂在标签 replace: false }); // 注册全局组件 // Vue.component(组件名, 组件构造器) // 组件名建议小写 并且包含一个 - Vue.component('my-component', MyComponent); new Vue({ el: '#example' }); // 注册全局组件 方法二. // 注册语法糖 // 可以省略组件构造器 直接传入对象 // replace : // false 不替换挂在标签 // true 替换挂在标签 Vue.component('my-component', { // 组件里的data对象 必须以函数形式返回 // 为了避免所有实例共享一个data 可以互相修改 data() { return { info: 'relative' } }, // 子组件的模板 // 可以是id, class模式 template : '#模板的id值 || class值' template: '<p>测试数据1</p>', replace: false }); new Vue({ el: '#example' }); // 注册局部组件 // 定义局部注册组件名称 let Parent = new Vue.extend({ // 根据局部组件创建根实例 el: '#example', components: { 'my-component': { // 组件里的data对象 必须以函数形式返回 // 为了避免所有实例共享一个data 可以互相修改 data() { return { info: 'absolute' } }, template: '<p>测试数据2</p>', replace: true } } }); </script> <div id="example"> // 把一个普通字符串传递给子组件的my-message变量 <my-component my-message="hello"></my-component> </div> <script> // props 父组件向子组件传递数据 Vue.component('my-component', { // my-message 用在子组件内需要去掉- 用驼峰方式 props: ['myMessage'], template: '<p>{{myMessage}}</p>', replace: false }); // 实例化 new Vue({ el: 'body' }); </script> <div> <input type="text" v-model="parentMsg"> // 绑定动态props v-bind:my-message="parentMsg" <my-component v-bind:my-message="parentMsg"></my-component> </div> <script> // props 父组件向子组件传递数据 Vue.component('my-component', { // my-message 用在子组件内需要去掉- 用驼峰方式 props: ['myMessage'], template: '<p>{{myMessage}}</p>', replace: false }); // 实例化 new Vue({ el: 'body', data: { parentMsg: '' } }); </script> // 父子组件的例子 <!-- 子组件模板 --> <template id="child-template"> <input v-model="msg"> <button v-on:click="notify">Dispatch Event</button> </template> <!-- 父组件模板 --> <div id="events-example"> <p>Messages: {{ messages | json }}</p> <child></child> </div> <script> // 注册子组件 Vue.component('child', { // 组件需要有自己的模板 template: '#child-template', data() { return { msg: 'hello' } }, methods: { notify: function () { if (this.msg.trim()) { this.$dispatch('child-msg', this.msg) this.msg = '' } } } }) // 初始化父组件 子组件在父组件内一起初始化 不用再单独初始化子组件 // 将收到消息时将事件推入一个数组 var parent = new Vue({ // 父组件的模板 el: '#events-example', data: { messages: [] }, // 在创建实例时 events 选项简单地调用 $on events: { 'child-msg': function (msg) { // 事件回调内的 `this` 自动绑定到注册它的实例上 this.messages.push(msg) } } }) </script> // 注册v-ref _________________________________________________ // 子组件 module.vue <template> <p>我是子组件</p> </template> <script> export default { methods: { alert() { console.log('父组件输出子组件的值'); } } }; </script> // 父组件 parent.vue <template> // 注册v-ref <user v-ref:child></user> </template> <script> import user from './1.vue'; export default { ready() { // this.$refs.child 包含注册有v-ref的子组件 // 调用子组件的alert方法 this.$refs.child.alert(); }, components: { user } } </script> // js入口 parent.js <script> import wangliang from './wangliang/wangliang.vue'; let parent = new Vue({ el: 'body', components: { wangliang } }) </script> // Slot 分发内容 _________________________________________________ // 单个Slot // 父模板 <template> <child> // 如果父组件内没有内容 显示子组件内容 <p>父组件</p> </child> </template> <script> import child from './child.vue'; export default { components: { child } } </script> // 子组件 <template> <div> <slot> 如果没有分发内容则显示我。 </slot> <h1>子组件</h1> </div> </template> // 入口js <script> import wangliang from './wangliang/wangliang.vue'; let parent = new Vue({ el: 'body', components: { wangliang } }) </script> // 具名 Slot _________________________________________________ // 父组件 <template> <child> // one会查找子组件里的 <slot name="one"></slot> // 如果找不到不显示默认内容 <slot name="one"></slot> 里的默认值 <p slot="one">One</p> // two会查找子组件里的 <slot name="two"></slot> <p slot="two">Two</p> <p>父组件</p> </child> </template> <script> import child from './child.vue'; export default { components: { child } } </script> // 子组件 <template> <div> <slot name="one"></slot> // 必须当父组件里一点内容都没有 才会显示默认内容 <slot>父组件没有内容 显示这里的值</slot> <slot name="two"></slot> </div> </template> // 入口js <script> import wangliang from './wangliang/wangliang.vue'; let parent = new Vue({ el: 'body', components: { wangliang } }) </script> // 动态组件 : 多个组件可以使用同一个挂载点 然后动态地在它们之间切换 使用保留的 // <component> 元素 动态地绑定到它的 is 特性 // 父组件 <template> // component 添加:is 通过改变变量currentView值可以动态切换组件 // keep-alive : 把切换出去的组件保留在内存中 避免重新渲染 <component :is="currentView" keep-alive></component> <div v-on:click="fn">点我切换</div> </template> <script> import child1 from './child1.vue'; import child2 from './child2.vue'; export default { data() { return { currentView: 'child1', currentView2: 'child2' } }, methods: { fn() { this.currentView = 'child2'; } }, components: { child1, child2 } } </script> // 子组件 1 <template> <div>测试数据 1</div> </template> // 子组件 2 <template> <div>测试数据 2</div> </template> // 入口js <script> import wangliang from './wangliang/wangliang.vue'; let parent = new Vue({ el: 'body', components: { wangliang } }) </script> // 组件和v-for _________________________________________________ <template> // v-for的参数可以通过 v-bind传递给子组件 <child1 v-for="item in data" v-bind:item="item" v-bind:index="$index"></child1> </template> <script> import child1 from './child1.vue'; export default { data() { return { data: [1, 2, 3] } }, components: { child1 } } </script> // 子组件 <template> // 显示父组件的item index {{item}} - {{index}} </template> <script> export default { props: ['item', 'index'] }; </script> // 组件自定义事件 @事件名 _________________________________________________ // 父组件 <template> <child1 @listener="method"></child1> </template> <script> import child1 from './child1.vue'; export default { methods: { method() { console.log('子组件事件触发'); } }, components: { child1 } } </script> // 子组件 <template> // 注册点击事件 触发事件名是父组件自定义的事件名 <div v-on:click="method">点击</div> </template> <script> export default { methods: { method() { // 通过$dispatch派发事件 触发父组件自定义事件 this.$dispatch('listener', '参数') } } }; </script> // 入口 <script> import wangliang from './wangliang/wangliang.vue'; let parent = new Vue({ el: 'body', components: { wangliang } }) </script>
12. nextTick、$nextTick、计算属性的缓存
<script> /** * vm.$log() : 打印整个ViewModel的数据 vm.$log('item') 打印某个属性 */ let vm = new Vue({ el: 'body', data: { msg: '123' }, ready() { // $nextTick : DOM更新后执行 this自动绑到实例 // 输出 '234' this.$nextTick(() => c(2, this.msg)); } }); // 修改msg的值 $nextTick会在之后执行 vm.msg = '234'; // 当天节点的内容 c(vm.$el.textContent); // nextTick的另外一种调用方式 // Vue.nextTick(函数) 这种方式不会把实例的this带进来 Vue.nextTick(() => c(2, vm.msg)); </script> // 页面里调用了5次 example 因为关闭了缓存 所以执行了5次 get <div class="example">{{msg}}-{{example}}</div> <div class="example">{{msg}}-{{example}}</div> <div class="example">{{msg}}-{{example}}</div> <div class="example">{{msg}}-{{example}}</div> <div class="example">{{msg}}-{{example}}</div> <script> let vm = new Vue({ el: 'body', data: { msg: '123' }, // 计算属性每次都会缓存结果 除非依赖改变 这里的依赖是this.msg computed: { example: { // cache : 关闭缓存 // 关闭缓存后每次调用example参数都会调用get方法 不再使用缓存 // 关闭缓存后 只有在JS离会多次执行 页面模板种的DOM只有依赖改变时才更新 // 如果打开缓存 不管执行多少次 get方法都只执行一次 cache: false, get() { return new Date().getTime() + this.msg; } } } }); vm.msg = '234'; setTimeout(() => { vm.msg = '345' }, 1000); </script>
13. 过滤器 Filter
// 过滤器可以接受参数 用空格分隔 <span>{{message | reverse "参数1" "参数2"}}</span> <script> // 自定义过滤器 // 方式一 : 全局定义 Vue.filter('过滤器名', 方法); Vue.filter('reverse', function (...value) { let [v1, v2, v3] = value; return v1 + v2 + v3; }); // 实例根元素 new Vue({ el: 'body', data: { message: '原始值' } }); </script>
14. 混合
<script> // 混合以一种灵活的方式为组件提供分布复用功能 混合对象可以包含任意的组件选项 // 定义混合对象 let myMixin = { // 定义组件的钩子在组件的钩子之前执行 created() { console.log('混合组件'); this.pull(); }, methods: { pull() { console.log('混合组件的pull事件'); } } }; export default { // 通过mixins 使用混合组件 mixins: [myMixin], created() { console.log('组件'); }, methods: { // 组件的pull将覆盖混合对象的pull属性 pull() { console.log('组件的pull事件'); } } } </script> <script> // 为所有组件注册created属性 Vue.mixin({ created() { var myOption = this.$options.myOption if (myOption) { console.log(myOption) } } }) // 创建根实例时 会执行全局注册的组件属性created // 输出hello new Vue({ el: 'body', myOption: 'hello!' }) </script>