当前位置: 代码迷 >> 综合 >> vue2.0 笔记
  详细解决方案

vue2.0 笔记

热度:95   发布时间:2023-10-17 07:05:27.0

VSCode 关于vue的扩展安装: Vue VSCode Snippets 、Vetur

    <div id="app">{
   {title}}</div><script src="./vue.js"></script><script>const app = new Vue({el: '#app',data() {return {title: 'hello,vue'}}})setTimeout(() => {app.title = 'mua~,vue'},1000);</script>

理解vue的设计思想

        1、数据驱动应用

        2、MVVM模式的践行者

MVVM框架的三要素:响应式、模板引擎及其渲染

        响应式:vue如何监听数据变化?

        模板:vue的模板如何编译和解析

        渲染:vue如何将模板转换为html

核心知识03--模板语法

        vue模板语法

                vue.js使用了基于HTML的模板语法,允许开发者声明式的将DOM绑定到底层Vue实例的数据,所有Vue.js的模板都是合法的HTML,所以能被遵循规范的浏览器和HTML解析器解析。 

插值文本 

        数据绑定最常见的形式就是使用"Mustache"语法(双大括号)的文本插值

范例:设置标题

     

    <div id="app"><h2><!-- 插值文本 -->{
   {title}}</h2></div><script src="./vue.js"></script><script>const app = new Vue({el: '#app',data() {return {title: 'hello,vue'}}})</script>

特性

        HTML特性不能使用"Mustache"语法,应该使用v-bind指令

范例-设置标题

    <div id="app"><!-- 特性、属性值绑定使用v-bind指令--><h2 v-blind:title="title">...</h2></div>

列表渲染

我们可以用v-for指令基于一个数组来渲染一个列表,v-for指令需要使用 item in items形式的特殊语法,其中 items 是源数据结构,而 item 则是被迭代的数组元素的别名。

范例:课程列表

    <div id="app"><!-- 条件渲染 --><p v-if="courses.length == 0">没有任何课程信息</p><!-- 列表渲染 --><ul><li v-for="c in courses" :key="c">{
   { c }}</div></ul></div><script src="./vue.js"></script><script>const app = new Vue({el: '#app',data() {return {courses: ['web全栈','web高级']}}})</script>

表单输入绑定

你可以用 v-modal 指令在表单 <input>、<textarea>、及 <select> 元素上创建双向数据绑定。它会根据控件类型自动选择正确的方法来更新元素。 v-modal 本质上是语法糖。它将转换为输入事件以更新数据,并对一些极端场景进行一些特殊处理。

范例:新增课程

   <!-- 表单输入绑定 --><input v-model="course" type="text" v-on:keydown.enter="addCourse"/>

事件处理

可以用 v-on 指令监听DOM事件,并在触发时运行一些JavaScript代码

范例:新增课程

    <!-- 事件处理 --><button v-on:click="addCourse">新增课程</button><script>const app = new Vue({el: '#app',data() {return {course:'',courses: ['web全栈','web高级']}},methods: {addCourse() {this.courses.push(this.course)this.course = ''}},})</script>

class与style绑定

操作元素的 class 列表和内联样式是数据绑定的一个常见需求,因为他们都是属性,所以我们可以用 v-bind 处理它们:只需要通过表达式计算出字符串结果即可。不过字符串拼接麻烦且易错。因此,在将 v-bind 用于 class 和 style 时,Vue.js做了专门的增强。表达式结果的类型除了字符串之外,还可以是对象或数组。

        <style>.active{background-color: #ddd;}</style><ul><!-- class绑定 --><li v-for="c in courses" :key="c" :class="{active: (selectedCourse === c)}" @click="selectedCourse = c">{
   { c }} </li><!-- style绑定 --><li v-for="c in courses" :key="c" :style="{backgroundColor: (selectedCourse === c)? '#ddd' : 'transparent'}" @click="selectedCourse = c">{
   { c }} </li></ul><script>const app = new Vue({el: '#app',data() {return {// 保存选中项selectedCourse:''}},})</script>

计算属性和监听器

模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护,此时就应该考虑计算属性和监听器。

范例:课程数量统计

        <p><!-- 绑定表达式 --><!-- 课程总数:{
   {courses.length + '门'}} --><!-- 计算属性 --><!-- 课程总数:{
   {total}} --><!-- 监听器 -->课程总数:{
   {totalCount}}</p><script>const app = new Vue({el: '#app',data() {return {totalCount:''}},compute: {total () {return this.courses.length + '门'}},watch: {// 下面这种不会生效,因为初始化时不会触发courses(newValue, oldValue) {this.totalCount = newValue.length + '门'}  },watch: {// 这种方式会在初始化时触发courses: {immediate: true,deep: true,handler(newValue, oldValue) {this.totalCount = newValue.length + '门'}}},})</script>

计算属性 VS 监听器

        1、监听器更通用,理论上计算属性能实现的监听器也能实现

        2、处理数据的场景不同,监听器适合一个数据影响多个数据,计算属性适合一个数据受多个数据影响

        3、计算属性有缓存行,计算所得的值如果没有变化不会重复执行

        4、监听器适合执行异步操作或比较大开销操作的情况

神奇的模板语法是如何实现的

在底层的实现上,Vue将模板编译成虚拟DOM渲染函数,结合响应系统,Vue能够智能地计算出最少需要重新渲染多少组件,并把DOM操作次数减到最少。

// 输出vue替我们生成的渲染函数一窥究竟
console.log(app.$options.render)
//它长这个样子
(functionanonymous(
){
with(this){return_c('div',{attrs:{"id":"app"}},[_c('h2',{attrs:
{"title":title}},[_v("\n"+_s(title)+"\n")]),_v("
"),_c('input',{directives:[{name:"model",rawName:"v-model",value:
(course),expression:"course"}],attrs:{"type":"text"},domProps:{"value":
(course)},on:{"keydown":function($event)
{if(!$event.type.indexOf('key')&&_k($event.keyCode,"enter",13,$event.key,"Enter"
))returnnull;returnaddCourse($event)},"input":function($event)
{if($event.target.composing)return;course=$event.target.value}}}),_v("
"),_c('button',{on:{"click":addCourse}},[_v("新增课程")]),_v(""),(courses.length
==0)?_c('p',[_v("没有任何课程信息")]):_e(),_v("
"),_c('ul',_l((courses),function(c){return_c('li',{class:{active:
(selectedCourse===c)},on:{"click":function($event){selectedCourse=c}}},
[_v(_s(c))])}),0)])}
})

结论:Vue通过它的编译器将模版编译成渲染函数,在数据发生变化时的时候再次执行渲染函数,通过对比两次执行结果得出要做的dom操作,模版中的魔法得以实现。

生命周期

每个Vue实例在创的时候都要经过一系列的初始化过程--例如,需要设置数据监听, 编译模板, 将实例挂载到DOM并在数据变化时更新DOM等, 称为Vue示例的生命周期

使用生命周期钩子

在Vue实例的生命周期过程中会运行一些叫做生命周期钩子的函数, 这给用户在不同阶段添加自己代码的机会.

范例: 异步获取课程列表

    <script>// 模拟异步数据调用接口function getCourses(){return new Promise(resolve => {setTimeout(() => {resolve(['web全栈','web高级'])}, timeout);})}const app = new Vue({el: '#app',data() {return {courses:[],}},// created钩子中调用接口async created () {const courses = await getCourses();this.courses = courses;}})</script>

生命周期结论:

        三个阶段: 初始化  更新  销毁

        初始化: beforeCreate  created  beforeMount  mounted

        更新:  beforeUpdate  updated

        销毁:  beforeDestroy  destroyed

使用场景分析:

        {

                beforeCreate(){}  // 执行时组件实例还未创建, 通常用于插件开发中执行一些初始化任务

                created(){}         // 组件初始化完毕, 各种数据可以使用, 通常用于异步数据获取

                beforeMount(){}    // 未执行渲染, 更新, dom未创建

                mounted(){}         // 初始化结束, dom已创建, 可用于获取访问数据和dom元素

                beforeUpdate(){}    // 更新前, 用于获取更新前各种状态

                updated(){}        // 更新后, 所有状态已是最新

                beforeDestroy(){}     // 销毁前,  可用于一些定时器或订阅的取消

                destroyed(){}        // 组件已销毁,作用同上

        }

组件化

组件是可复用的Vue实例,带有一个名字,我们可以在一个通过new Vue 创建的Vue根实例中,把这个组件作为自定义元素来使用.

组件注册、使用以及数据传递

Vue.component(name, options)  // 可用于组件注册

范例: 提取课程新增和课程列表组件

    <!-- 列表渲染 --><course-list :courses="courses"></course-list><script>// 注册course-list组件Vue.component('course-list',{data() {return {selectedCourse: ''}},props: {courses: {type: Array,default: [] },},template: `<div><!-- 条件渲染 --><p v-if="courses.length === 0">没有任何课程信息</p><!-- 列表渲染 --><ul><!-- class绑定 --><li v-for="c in courses" :key="c" :class="{active: (selectedCourse === c)}" @click="selectedCourse = c">{
   { c }} </li></ul></div>`})const app = new Vue({el: '#app',data() {return {courses: ['web全栈','web高级'],}}})</script>

自定义事件及其监听

当子组件需要和父组件进行通信,  可以派发并监听自定义事件

范例: 新增课程组件派发新增事件

        <!-- 课程添加组件调用 --><course-add @add-course="addCourse"></course-add>// 注册course-add组件Vue.component('course-add', {data() {return {course: '', // 将course从父组件提取到course-add维护}},template: `<div><!-- 表单输入绑定 --> <input v-model="course" @keydown.enter="addCourse"/>   <!-- 事件处理 --><button v-on:click="addCourse">新增课程</button></div>`,methods: {// 回调函数接收事件参数addCourse() {// 发送自定义事件通知父组件// 注意事件名称定义时不要有大写字母出现this.$emit('add-course', this.course )this.course = ''}}})const app = new Vue({el: '#app',data() {return {courses: ['web全栈','web高级'],}},methods: {addCourse(course) {this.courses.push(course)}}})

在组件上使用v-modal

范例: 改造course-add为支持v-modal的版本

        <!-- 自定义组件支持v-modal需要实现内部的input的:value和@input  --><course-add v-modal="course" @add-course="addCourse"></course-add><script>// 注册course-add组件Vue.component('course-add', {props:['value'],template: `<div><!-- 需要实现input的:value和@input --> <input :value="value" @input="onInput" @keydown.enter="addCourse"/>   <!-- 事件处理 --><button v-on:click="addCourse">新增课程</button></div>`,methods: {addCourse() {// 派发事件不再选哟传递数据this.$emit('add-course', this.course )},onInput (e) {this.$emit('input', e.target.value)}},})const app = new Vue({el: '#app',data() {return {course:'',  // 还原course// 保存选中项selectedCourse:'',courses: ['web全栈','web高级'],totalCount:''}},methods: {addCourse() {this.courses.push(this.course)}},})</script>

v-model 默认转换是:value和@input, 如果想要修改这个行为, 可以通过定义model选项

Vue.component('course-add', {model: {prop:'value',event: 'change'}
})

通过插槽分发内容

        通过使用vue提供的 <slot> 元素可以给组件传递内容

范例:弹窗组件

    <style>.message-box{padding: 10px 20px;background: #4fc08d;border: 1px solid #42b983;}.message-box-close {float: right;}</style><!-- 插槽实现内容分发 --><message :show.sync="show" @update-show="updateShow">新增课程成功!</message><script>// 注册message组件 slot做为占位符Vue.component('message', {props: ['show'],template: `<div class="message-box" v-if="show"><slot></slot><span class="message-box-close" @click="$emit('update-show',     false)">X</spam></div>`})const app = new Vue({el: '#app',data() {return {show: false, // 提示框状态course:'',  // 还原course// 保存选中项selectedCourse:'',courses: ['web全栈','web高级'],totalCount:''}},methods: {// 还原addCourseaddCourse() {this.courses.push(this.course)// 提示框新增信息this.show = true},updateShow (bool) {this.show = bool;}},})</script>

如果存在多个独立内容要分发,可以使用具名插槽v-slot:name

范例: 添加一个title部分

        <!-- 插槽实现内容分发 --><message :show.sync="show" @update-show="updateShow"><template v-slot:title>恭喜</template><template>新增课程成功!</template></message>// 注册message组件 slot做为占位符Vue.component('message', {props: ['show'],template: `<div class="message-box" v-if="show"><strong><slot name="title"></slot/></strong><slot></slot><span class="message-box-close" @click="$emit('update-show', false)">X</spam></div>`})

Vue组件化的理解3

组件化是Vue的精髓,Vu而英勇就是由一个个组件构成的, Vue的组件化设计到的内容非常多, 可以从一下几点进行阐述.

定义: 组件是可复用的Vue实例, 准确讲它们是VueComponent的实例, 继承自Vue

优点: 从上面案例可以看出组件化可以增加代码的复用性、 可维护性、和可测试行

使用场景: 什么时候使用组件?以下分类可作为参考

        1、通用组件: 实现最基本的功能, 具有通用性  复用性, 例如按钮组件、输入框组件、布局组件等

        2、业务组件: 它们完成具体业务, 具有一定的复用性, 例如登陆组件, 轮播图组件

        3 、页面组件: 组织应用各部分独立内容, 需要时在不同页面组件间切换, 例如列表页, 详情页组件

如何使用组件

        1、 定义: Vue.component() 、 comonents选项、sfc

        2、分类: 有状态组件, functional 、 abstract

        3、 通信 props、$emit() / $on()、 provide/inject、 $children / $parent/、 $root、 $attrs、 $listeners

        4、内容分发: <slot>, <template>, v-slot

        5、 使用及优化: is, keep-alive, 异步组件

组件的本质

        vue中的组件经理如下过程

        组件配置 => Vu二Component实例 => render() => Virtual DOM => DOM

拓展知识--可复用性

过滤器

Vue.js允许你自定义过滤器,可被用于一些常见的文本格式化。过滤器可以用在两个地方:双花括号插值和v-bind表达式。过滤器应该被添加在JavaScript表达式的尾部,由"管道"符号指示

<!-- 双花括号中 -->
{
   { message | capitalize }}<!-- v-bind 中-->
<div v-bind:id="rawId | formatId"></div>

范例:货币符号使用

    <div id="app"><h2>Web 开发工程师最低薪资是 {
   { number | currency('¥') }}</h2></div><script>const app = new Vue({el: '#app',data() {return {number: 10000}},filters: {currency (value, symbol = '$') {return symbol + value;}}})</script>

自定义指令

除了核心功能默认内置的指令(v-modal 和 v-show),Vue也允许自定义指令。注意,在Vue2.0中,代码复用和抽象的主要形式是组件。然而,有的情况下,你仍然要对普通DOM元素进行底层操作,这时候就会用到自定义指令

范例:输入框获取焦点

directives/focus.js

export default {directiveName: 'focus',options: {inserted(el) {el.focus();}}
}

directives/permision.js

export default {directiveName: 'permision',options: {inserted(el,binding) {if (binding.value !== 'admin') {el.parentElement.removeChild(el);}}}
}

directives/index.js

import focus from './focus';
import permision from './permision';export default {focus,permision
}

 main.js

import Vue from 'vue'
import App from './App.vue'
// 自定义指令
import directives from './directives'Vue.config.productionTip = false
Vue.prototype.$bus = new Vue()const { focus, permision } = directives// 获取焦点自定义指令
Vue.directive(focus.directiveName, focus.options)
// 权限自定义指令
Vue.directive(permision.directiveName, permision.options)new Vue({render: h => h(App),
}).$mount('#app')

 CourseAdd.vue

 <!-- 表单输入绑定 --><input v-model="course" type="text" v-on:keydown.enter="addCourse" @input="onInput" v-focus/>

 DirectiveBtn.vue

<template><div v-permision="'user'"><button>自定义指令按钮</button></div>
</template>

 

 

Vue.set 、Vue.delete 响应式数据添加删除

Vue.set: 向响应式数据中添加一个属性,并确保这个新的响应式数据同样是相应式的,且触发视图更新。使用方法:Vue.set(target, propertyName/index, value)

范例:设置商品价格

<template><div><!-- 添加批量加格更新 --><p><input type="text" v-model.number="price"><button @click="batchUpdate">批量更新价格</button></p><ul><!-- class绑定 --><li v-for="c in courses" :key="c" :class="{active: (selectedCourse === c)}" @click="selectedCourse = c">{
   { c.name }} - ¥{
   {c.price || 0}} </li><!-- style绑定 --><!-- <li v-for="c in courses" :key="c" :style="{backgroundColor: (selectedCourse === c)? '#ddd' : 'transparent'}" @click="selectedCourse = c">{
   { c }} </li> --></ul></div>
</template><script>export default {props: {courses: {type: Array,required: true,default: function () {return []}},},data() {return {// 保存选中项selectedCourse:'',price: 0    // 增加加格数据}},methods:{batchUpdate(){this.courses.forEach(course => {this.$set(course, 'price', this.price)})}}}
</script><style scoped>.active{background-color: #ddd;}
</style>

Vue.delete

删除对象的属性。如果对象是响应式的,确保删除能触发更新视图

使用方法:Vue.delete(target, property/index)

Vue事件总线 $bus

通过在Vue原型上添加一个Vue实例作为事件总线,实现组件之间相互通信,而且不受组件之间关系的影响

Vue.prototype.$bus = new Vue()

范例:关闭消息框

Message.vue

<template><div class="message-box" v-if="show"><strong><slot name="title"></slot></strong><slot></slot><span class="message-box-close" @click="$emit('update-show', false)">X</span></div>
</template><script>export default {props: ['show'],mounted () {this.$bus.$on('message-close', () => {this.$emit('update-show', false)});},}
</script><style scoped>.message-box{padding: 10px 20px;background: #4fc08d;border: 1px solid #42b983;}.message-box-close {float: right;cursor: pointer;}
</style>

 ClearMessage.vue

<template><button class="toolbar" @click="clearMessage">清空提示框</button>
</template><script>export default {name: 'ClearMessage',methods:{clearMessage(){// 派发关闭事件this.$bus.$emit('message-close', false)}},}
</script><style lang="scss" scoped></style>

 main.js

import Vue from 'vue'
import App from './App.vue'Vue.config.productionTip = false
Vue.prototype.$bus = new Vue()new Vue({render: h => h(App),
}).$mount('#app')

vm.$once

监听一个自定义事件,但是只触发一次。一旦出发之后,监听器就会被移除。

vm.$once('test', function(msg) {console.log(msg)
})

vm.$off

移除自定义事件监听器

        1、如果没有提供参数,则移除所有的事件监听器

        2、如果只提供了事件,则移除该是按所有的监听器

        3、如果同时提供了事件与回调,则只移除这个回调的监听器

vm.$off()    // 移除所有的事件监听器
vm.$off('test')    // 移除该事件所有的监听器
vm.$off('test', callback)    // 只移除这个回调的监听器

ref、vm.

$refs

组件或元素引用:ref和vm.$refs, ref被用来给元素或子组件注册引用信息,引用信息将会注册在父组件的$refs对象上。如果在普通的DOM元素上使用,引用指向的就是DOM元素;如果用在子组件上,引用就指向该子组件

<input type="text" ... ref="inp"> mounted(){ // mounted之后才能访问到inp this.$refs.inp.focus() 
}

 范例:改造message组件用打开、关闭方法控制显示

<!--自定义组件引用--> 
<message ref="msg">新增课程成功!</message> <script> Vue.component('message', { // 组件显示状态 data() { return { show: false } },template: ` <div class="message-box" v-if="show"> <slot></slot> <!--toggle内部修改显示状态--> <span class="message-box-close" @click="toggle">X</span> </div> `,// 增加toggle方法维护toggle状态 methods: { toggle() { this.show = !this.show; } },// 修改message-close回调为toggle mounted () { this.$bus.$on('message-close', this.toggle); }, })const app = new Vue({methods: { addCourse() { // 使用$refs.msg访问自定义组件 this.$refs.msg.toggle() } } })
</script>

 注意:

        1、ref是作为渲染结果被创建的,在初始渲染时不能访问它们

        2、$refs不是响应式的,不要试图用它在模板中做数据绑定

        3、当v-for用于元素或组件时,引用信息将是包含DOM节点或组件实例的数组

Vue-cli

快速原型开发----可以使用 vue server 和 vue build 命令对单个.vue文件进行快速原型开发

安装: 

npm install -g @vue/cli-service-global

准备一个原型内容---- vue serve ------ 启动一个服务并运行原型----- vue serve hello.vue

vue serve hello.vue

要求:vue-cli须在4.x

vuec-cli升级 

npm uninstall -g vue-cli     // 卸载npm install -g @vue/cli
 npm list -g --depth 0    // 查看npm全局安装模块详细信息

Vuex

Vuex是一个专为Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以可预测的方式发生变化。

安装

vue add vuex

1、state: 应用全局状态定义在state中

2、mutation:修改state只能通过mutation

3、action:类似于mutation,不同在于action提交的是mutation,而不是直接变更状态,action可以包含任意异步操作

4、getter:可以使用getters从store的state中派生出一些状态

5、插件:Vuex的store接受plugins选项,这个选项暴漏出每次mutation的钩子。Vuex插件就是一个函数,它接收store作为唯一参数

vuex相关文件:

main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import focus from './directives/focus'
import store from './store'
import './plugins/element.js'Vue.config.productionTip = false
Vue.prototype.$bus = new Vue()
Vue.directive('focus', focus)new Vue({router,store,render: h => h(App)
}).$mount('#app')

/store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
import user from './user'
import persist from './plugins/persist'Vue.use(Vuex)export default new Vuex.Store({modules: {user},strict: true,plugins: [persist]
})

/store/user.js

import {addAuthRoute} from '@/router/index'export default {namespaced: true, // 设置独立命名空间,避免命名冲突state: {isLogin: false,username: ''},mutations: {login(state, username) {state.isLogin = truestate.username = username},logout(state) {state.isLogin = falsestate.username = ''},},getters: {welcome: state => state.username + ',欢迎回来'},actions: {// 参数1是vuex传递的上下文context:{commit, dispatch, state}login({ commit }, username) {// 模拟登录api调用,1s钟以后如果用户名是admin则登录成功return new Promise((resolve, reject) => {setTimeout(() => {if (username === 'admin') {commit('login', username)// 动态添加认证路由addAuthRoute()resolve()} else {reject()}}, 1000);})}}
}

/store/plugins/persist.js   实现登录状态持久化

export default store => {// store初始化的时候,将存储在localStoreage中的状态还原if (localStorage) {const user = JSON.parse(localStorage.getItem('user'))if (user) {store.commit('user/login', user.username)}}// 如果用户相关状态发生变化,自动存入localStoreagestore.subscribe((mutation, state) => {// {type:'user/login'}// {type:'user/logout'}// {type:'cart/addCart'}if(mutation.type === 'user/login') {const user = JSON.stringify(state.user)localStorage.setItem('user', user)} else if (mutation.type === 'user/logout') {localStorage.removeItem('user')}})
}

mapState、mapMutations、mapActions、mapGetters使用

login.js

<template><div><button @click="login" v-if="!isLogin">登录</button><button @click="logout" v-else>注销</button><p>{
   {welcome}}</p></div>
</template><script>import {mapState, mapGetters, mapMutations, mapActions} from 'vuex'export default {methods: {login() {// window.isLogin = true;// 提交mutation变更状态//   this.$store.commit('user/login')// 派发动作,触发actions// this.$store.dispatch("user/login", "admin").then(() => {this["user/login"]("admin").then(() => {this.$router.push(this.$route.query.redirect);}).catch(() => {alert('用户名或密码错误,请重试!')});this.LOGIN("admin")},logout() {// window.isLogin = false;this.$store.commit("user/logout");this.$router.push("/");},...mapMutations('user',{LOGIN:'login',LOGOUT:'logout'}),...mapActions(['user/login', 'user/logout'])},computed: {// isLogin() {//   return this.$store.state.user.isLogin;// }...mapState('user', ['isLogin']),...mapGetters('user',['welcome'])}
};
</script><style lang="scss" scoped>
</style>