🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# Vue基础知识 [TOC] ### computed属性 #### **为什么要使用计算属性** 模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护,对于任何复杂逻辑,你都应当使用**计算属性**。 计算属性是根据data中已有的属性,计算得到一个新的属性,创建一个计算属性要通过computed对象来创建 ```html <div id='app'> <p>{{ hi }}</p> </div> <script> var vm = new Vue({ el: '#app', data: { message1: 'hello', message2: 'world' }, computed: { // hi 就是一个计算属性,hi虽然是一个函数形式,但是在使用的时候,是通过属性的方式使用,如果通过函数调用方式的使用话,会报这个 计算属性不是一个函数。 hi() { return this.message1 + this.message2; } } }) </script> ``` #### computed属性 vs `methods`方法 我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是**计算属性是基于它们的依赖进行缓存的**,而方法每当触发重新渲染时,调用方法将**总会**再次执行函数,不会依赖缓存。 #### 计算属性 vs 侦听属性 `watch` Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:**侦听属性**。当你有一些数据需要随着其它数据变动而变动时,你很容易滥用 `watch`然而,通常更好的做法是使用计算属性而不是使用 `watch`属性。 `watch`属性用来侦听data属性中的数据,只要被侦听的数据发生变化,就能触发相应的函数,被触发的函数,就是需要侦听data数据中的属性命名的函数,这个函数包含两个参数一个是新值,一个是旧值。 ```html <body> <div id='app'> <h3>computed</h3> 姓: <input type="text" v-model="firstName"> 名: <input type="text" v-model="secondName"> <br> <p> 全名:{{ hi }}</p> <h3>watch</h3> <p> 全名:{{ fullName }}</p> </div> <script> var vm = new Vue({ el: '#app', data: { firstName: '', secondName: '', fullName: '' }, computed: { hi() { return this.firstName + this.secondName; } }, watch: { // firstName函数名称就是data属性中firstName属性名 // newVal 新值 oldVal 旧值 firstName(newVal, oldVal) { // console.log(newVal, oldVal); this.fullName = newVal + this.secondName; }, // 上同 secondName: function(newVal, oldVal) { this.fullName = this.firstName + newVal; } } }) </script> </body> ``` 两种方式的最终结果确实是完全相同的,但是什么时候使用计算属性,什么时候使用watch属性呢? > 能够使用computed属性实现的结果,就使用computed属性,不要使用`watch`,因为`watch`是一直在侦听数据的变化,对内存性能开销比较大,而**计算属性是基于它们的依赖进行缓存的**,所以从性能上比watch开销要小很多。 那什么时候使用`watch`属性呢? > 当需要执行异步操作的时候,就要使用watch属性,这个是computed属性无法做到的。 #### 深度侦听 > 如果是要侦听data中的一个对象的属性,那么这时候就不能使用普通的(浅)侦听模式了,需要进行深度侦听 ```html <body> <div id='app'> <h3>深度watch</h3> 姓名:<input type="text" v-model="userInfo.name"> <!-- <p> 用户姓名为:{{ userInfo.name }}</p> --> </div> <script> var vm = new Vue({ el: '#app', data: { userInfo: { name: 'xz' } }, watch: { // 如果是要侦听data中的一个对象的属性,那么这时候以下写法不能被执行,需要进行深度侦听。 // userInfo(newVal, oldVal) { // console.log(newVal, oldVal); // }, // 深度侦听 // 被深度侦听data属性中的对象 userInfo: { // 深度侦听的处理函数 handler(val, oldVal) { console.log(val.name); }, // 深度侦听开启 deep: true } } }) </script> </body> ``` ### axios(爱咳嗽丝) ### 组件 #### 组件的创建方式 ```html <body> <!-- 模板的要定义在vue的view视图之外 --> <template id="tmplt"> <div> 我是componentD </div> </template> <div id='app'> <!-- 采用驼峰命名创建组件,在使用组件的时候,要加上短横杠 - 。 --> <component-a></component-a> <component-b></component-b> <component-c></component-c> <component-d></component-d> </div> <script> // 组建的创建方式:共三种 // 第一种(使用extend和component共同创建组件) var componentOne = Vue.extend({ // 注意这里的模板template中只能有一个根节点,不能存在多个根节点。 template: "<div>我是componentA<p>我是p标签中的内容</p></div>" // 以下是错误的 // template: "<div>我是componentA</div><p>我是p标签中的内容</p>" }); // 采用驼峰命名创建组件,在使用组件的时候,要加上短横杠 - 。 Vue.component('componentA', componentOne); // 等价于 Vue.component('componentB', Vue.extend({ template: "<div>我是componentB</div>" })); // 第二种方式(直接使用component创建,通过这里可以看出,Vue.component方法的本质上还是调用了Vue.extend方法了) Vue.component('componentC', { template: "<div>我是componentC</div>" }); // 第三种方式(使用指定的模板创建, 模板的要定义在vue的view视图之外) Vue.component('componentD', { template: '#tmplt' }) var vm = new Vue({ el: '#app', data: { }, }) </script> </body> ``` #### 父子组件的创建 ```html <body> <div id='app'> <father></father> </div> <script> Vue.component('father', { template: ` <div> <div>我是父组件</div> <son></son> </div> `, components: { son: { template: ` <div>我是子组件</div> ` } } }) var vm = new Vue({ el: '#app', data: { }, }) </script> </body> ``` #### 父组件向子组件传值 ```html <body> <div id='app'> <father></father> </div> <script> Vue.component('father', { template: ` <div> <div>我是父组件,我的儿子叫{{mySonName}}</div> <son :myName="mySonName"></son> </div> `, data() { return { mySonName: '小宏' } }, components: { son: { props: ['myName'], // props 用来接收从父组件传递过来的值 template: ` <div>我是子组件,我叫{{myName}}</div> ` } } }) var vm = new Vue({ el: '#app', data: { }, }) </script> </body> ``` #### 子组件向父组件传值 ```html <body> <div id='app'> <father></father> </div> <script> Vue.component('father', { template: ` <div> <div>我是父组件,我的儿子叫{{mySonName}}</div> <son @sendFatherName="getSonName"></son> </div> `, data() { return { mySonName: '' } }, methods: { getSonName(val) { console.log(val); this.mySonName = val; } }, components: { son: { template: ` <div> <div>我是子组件,我的名字叫{{myName}}</div> <button @click="sendData">点击把我的名字告诉给父组件</button> </div> `, data() { return { myName: '小明' } }, methods: { // 子组件向父组件传值,需要通过事件向父组件传递值,在事件中需要用到$emit()方法,这个方法有两个参数,第一个参数是事件的名称,第二个参数是需要传递的数据。 sendData() { this.$emit('sendFatherName', this.myName) } } } } }) var vm = new Vue({ el: '#app', data: { }, }) </script> </body> ``` #### 兄弟组件之间的传值 ```html <body> <div id='app'> <father></father> </div> <script> // 创建一个事件总线(相当于中间人) var bus = new Vue(); Vue.component('father', { template: ` <div> <son></son> <brother></brother> </div> `, components: { son: { template: ` <div> 我的兄弟叫{{ myBrotherName }} </div> `, data() { return { myBrotherName: '' } }, // 页面加载完成后 mounted() { // 通过事件总线中的$on()方法监听兄弟组件传递过来的事件和值 bus.$on('sendBrotherName', val => { // console.log(val); this.myBrotherName = val; }) } }, brother: { template: ` <div> 我叫{{ myName }} <button @click="snedMyName">点击告诉我哥哥我的名字</button> </div> `, data() { return { myName: '小明' } }, methods: { snedMyName() { // 通过事件总线,触发$emit()方法,兄弟组件之间可以传递值 bus.$emit('sendBrotherName', this.myName); } } } } }) var vm = new Vue({ el: '#app', data: { }, }) </script> </body> ``` #### 动态创建组件 ```html <body> <div id='app'> <ul> <li @click="active='index'"><a href="#">首页</a></li> <li @click="active='fe'"><a href="#">前端</a></li> <li @click="active='back'"><a href="#">后端</a></li> <li @click="active='ui'"><a href="#">UI</a></li> </ul> <!-- 通过component 动态创建组件 --> <component :is="active"></component> </div> <script> Vue.component('index', { template: ` <div> 我是首页 </div> ` }); Vue.component('fe', { template: ` <div> 我是前端 </div> ` }); Vue.component('back', { template: ` <div> 我是后端 </div> ` }); Vue.component('ui', { template: ` <div> 我是UI </div> ` }) var vm = new Vue({ el: '#app', data: { active: '' }, }) </script> </body> ``` ### 路由 #### 路由和路由参数 ```html <body> <div id='app'> <div> <ul> <li> <router-link to="/index">首页</router-link> </li> <li> <router-link to="/fe">前端</router-link> </li> <li> <router-link to="/back/90">后端</router-link> </li> </ul> </div> <!-- 4、使用 router-view组件 把路由对应的组件进行渲染 --> <router-view></router-view> </div> <script> // 1、定义(路由)组件(准备路由需要的组件) var index = Vue.component('index', { template: ` <div> 首页 </div> ` }); var fe = { template: `<div>前端</div>` } var back = { template: `<div>后端{{$route.params.id}}</div>`, mounted() { console.log(this.$route.params.id); // => 90 } }; // 2、创建路由对象,在这个对象中会配置路由规则,每个路由会映射(对应)一个组件 var router = new VueRouter({ routes: [{ name: 'index', path: '/index', component: index }, { name: 'fe', path: '/fe', component: fe }, { name: 'back', path: '/back', component: back }] }) console.log(router); var vm = new Vue({ el: '#app', data: { }, // 3、在vue实例中注入路由 router }) </script> </body> ``` #### 响应(监听)路由参数变化 ```html <body> <div id='app'> <div> <ul> <li> <router-link to="/index">首页</router-link> </li> <li> <router-link to="/fe">前端</router-link> </li> <li> <router-link to="/back/90">后端</router-link> </li> <li> <router-link to="/back/100">后端</router-link> </li> </ul> </div> <!-- 4、使用路由把路由组件渲染出来 --> <router-view></router-view> </div> <script> // 1、定义路由组件 var index = Vue.component('index', { template: ` <div>首页</div> ` }); var fe = { template: `<div>前端</div>` }; var back = { template: `<div>后端{{$route.params.id}} -- {{ info }}</div>`, // mounted() { // console.log(this.$route.params.id); // => 90 // }, data() { return { info: '' } }, // 当使用路由参数时,例如从 /user/foo 导航到 /user/bar,原来的组件实例会被复用。 // 因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会再被调用。 // 复用组件时, 想对路由参数的变化作出响应的话, 你可以简单地 watch(监测变化) $route 对象: watch: { // 响应路由参数变化 '$route' (to, from) { // console.log(to); // console.log(from); if (to.params.id === '90') { this.info = 'Docker' } else if (to.params.id === '100') { this.info = 'jdbc' } } } }; // 2、创建路由对象,在路由对象中配置路由规则 var router = new VueRouter({ // 2、1 定义路由 routes: [{ name: 'index', path: '/index', component: index }, { name: 'fe', path: '/fe', component: fe }, { name: 'back', path: '/back/:id', component: back }, ] }) var vm = new Vue({ el: '#app', data: { }, // 3、在vue实例中注入路由 router, }) </script> </body> ``` #### 嵌套路由和编程式导航 ```html <body> <div id='app'> <div> <ul> <li> <router-link to="/index">首页</router-link> </li> <li> <router-link to="/fe">前端</router-link> </li> <li> <router-link to="/back/90">后端</router-link> </li> <li> <router-link to="/back/100">后端</router-link> </li> </ul> </div> <!-- 4、使用路由把路由组件渲染出来 --> <router-view></router-view> </div> <script> // 1、定义路由组件 var index = Vue.component('index', { template: ` <div>首页</div> ` }); var fe = { template: `<div>前端</div>` }; var back = { template: `<div> <p>后端{{$route.params.id}}</p> <p>{{ info }}</p> <button @click="goTo">点击查看更多技术点</button> <router-view></router-view> </div>`, // mounted() { // console.log(this.$route.params.id); // => 90 // }, data() { return { info: '' } }, // 当使用路由参数时,例如从 /user/foo 导航到 /user/bar,原来的组件实例会被复用。 // 因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会再被调用。 // 复用组件时, 想对路由参数的变化作出响应的话, 你可以简单地 watch(监测变化) $route 对象: watch: { // 响应路由参数变化 '$route' (to, from) { // console.log(to); // console.log(from); if (to.params.id === '90') { this.info = 'Docker' } else if (to.params.id === '100') { this.info = 'jdbc' } } }, methods: { goTo() { this.$router.push({ name: 'skill' }) console.log(this.$router); } } }; var skill = { template: ` <div> 这里有很多的技术点哈</div> ` }; // 2、创建路由对象,在路由对象中配置路由规则 var router = new VueRouter({ // 2、1 定义路由 routes: [{ name: 'index', path: '/index', component: index }, { name: 'fe', path: '/fe', component: fe }, { name: 'back', path: '/back/:id', component: back, children: [{ name: 'skill', path: 'skill', component: skill }] }, ] }) var vm = new Vue({ el: '#app', data: { }, // 3、在vue实例中注入路由 // router: router, // 等价于下面 router, }) </script> </body> ``` ### vue项目 ## 将原生事件绑定到组件 你可能有很多次想要在一个组件的根元素上直接监听一个原生事件。这时,你可以使用 `v-on` 的 `.native` 修饰符: ```vue <base-input v-on:focus.native="onFocus"></base-input> ``` ```html <el-dropdown-menu slot="dropdown"> <el-dropdown-item @click.native="logOUt">登出</el-dropdown-item> </el-dropdown-menu> ```