VUE2学习

1,379 阅读5分钟

MVVM模式

视图与模型的双向绑定,即数据的变动会导致页面的变化。视图与模型分开。

graph TD
view --> viewmodel --> model --> viewmodel --> view

vue初体验

将其引入html中即可使用vue相关语法。

<!-- 开发环境版本,包含了有帮助的命令行警告 --> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<!-- 生产环境版本,优化了尺寸和速度 --> <script src="https://cdn.jsdelivr.net/npm/vue@2"></script>

实例与数据绑定

//app代表Vue实例
var app = new Vue({
    el:'#dom' // 页面存在的DOM元素
    data:{} // 数据绑定,值为一个js对象
})
console.log(app.$el,app.data对象的对象key) // 访问方式

插值和表达式

双大括号{{}}是文本插值方法,可以实现数据双向绑定,可以使用三元运算计算结果,可以使用管道符计算结果{{data | formatDate}}(通过右边的函数计算结果)

标签属性(指令和事件)

  • v-html 直接输出html(需要xss处理)
  • v-pre 跳过编译过程
  • v-bind 动态更新html元素上的属性=语法糖 :html元素属性
    • 设置对象,可以动态切换值:class="{'active':isActive}"
    • 设置方法,可以使用计算属性来getter值
    • 设置数组,:class="[activecss,errorcss]"
    • :style="以上三种" 设置内联样式方法
  • v-on 绑定事件监听器=语法糖 @js事件
  • v-cloak 会在实例结束编译时从绑定的html元素上移除
  • v-once 元素或组件只会渲染一次
  • key属性会使元素重新渲染
  • v-if/v-show show适合频繁切换条件,if适合不经常改变的场景
  • v-for 改变原始数组值的方法都会导致元素的重新渲染
  • @click 点击事件语法糖 如果有参数会将event传入方法。vue特殊变量$event用于访问原生dom事件
  • v-model 表单元素双向绑定数据,其值是表单的默认值;默认找的是它的value属性,如果没有就找text的值
  • v-model.lazy 只会在离开焦点时渲染
  • v-model.trim 去掉字符串头尾空格
  • :value 表单值的动态修改
<div id ="app">
<select v-model ="selected">
<option : value ="{ number : 123 }"> 123</option>
</select>
{{ selec;ted.number }}
</div>
<script>
var app =new Vue ({
el :'#app',
data : {
   selected :''
})
</script>
  • @input 处理输入框

自定义指令

有如下钩子函数

  • bind:只调用一次,可以用在初始化动作
  • inserted:被绑定元素插入父节点时调用
  • update:被绑定元素所在模板更新时调用
  • componentUpdate:被绑定元素所在模板完成一次更新周期时调用
  • unbind:只调用一次,指令与元素解绑时调用 每个钩子函数有如下参数
  • el 指令绑定的元素
  • binding(对象。以下面的demo为例)
    • name 指令名 test
    • value 指令绑定的值 message的值
    • oldValue 执行绑定前一个值
    • expression 绑定值的字符串形式 message
    • arg 传给指令的参数 msg
  • vnode 虚拟节点
  • oldVnode 上一个虚拟节点
// test是指令名,message的值就是指令值,
<div v-test:msg.a.b="message"></div>
Vue.directive('test',{
    bind:function(el,binding,vnode){}
})

修饰符

它的值可以是一个方法,也可以为空。

<!--阻止冒泡-->
<a @click.stop ="handle"></a>
<!--提交事件不再重新加载-->
<a @submit.prevent ="handle"></a>
<a @click.stop.prevent ="handle"></a>
<!--添加事件侦听器时使用事件捕获模式-->
<div @click.capture ="handle ”> ... </div>
<!--只触发一次-->
<div @click.once ="handle ”> ... </div>
<!--回车时触发-->
<div @key.13 ="handle ”> ... </div>
<!--配置具体的按键。使用方式@keyup.fl-->
Vue.config.keyCodes.fl = 112;

组件

  1. 关于组件的props类型验证有:String、Number、Boolean、Object、Array、Function。同时支持自定义验证validator:(data)=>{return data>10}
//全局组件声明方式
<div id ="app" >
    <my-component maessage="父级"></my-component>
</div>
<script>
var child = {template:'<div>局部</div>'}
Vue.component ('my-component',{
    props:['message'],与data return的区别就是,它的值来自于父级,而data的数据是属于它本身。它们的传递是单向的,只能是父到子
    template:'<div>test</div>',// template必须由一个html元素包裹
    data:()=>{// 与实例的区别就是 组件data必须是函数,而且有返回值
        return {...}
    }
)}
var app = new Vue({
    el :'#app',
    components:{'my-component':child} // 局部组件声明方式
})
</script>
  1. 组件通信
graph TD
子1 --> 子2 --> 子1  -->父--> 子2 
  • 子组件通过$emit()来触发(创建)自定义组件上的定义事件
  • 父组件通过$on()来监听子组件的事件
  • $refs在父组件中获取子组件索引集合
  • $parent在自定义组件中获取父组件集合
//展示计数结果
<my-component @increase="getTotal"></my-component>

//my-component组件内方法定义
template:'<button @click="handleClick">+l</button>'
methods:{
    handleIncrease:()=>{
    //在自定义组件内部触发外部定义的increase事件
        this.$emit('increase',this.counter)
    }
}

//父类实例
var app = new Vue({
methods:{
    getTotal:(total){}
}
})

一种总线模式的子父组件通信方式。即在父子的通信通过一个总线bus连接

<component-a></component-a>
//总线
var bus = new Vue();

template:'<button @click="handle"></button>',
methods:{
    handle:()=>{
    //通过bus触发on-message事件
        bus.$emit('on-message','信息')
    }
}

mounted:()=>{
//监听on-message事件
    bus.$on('on-message',(data)=>{})
}

计算属性

  • 处理复杂逻辑处理时,使用计算属性。即将数据与逻辑分开,只返回结果就可以了
  • 计算属性可以在多个vue实例中交替使用,当一个实例数据发生变动时,另一个实例将发生变化
  • 计算属性具有缓存性,数据发生时,它才会重新取值计算。而不是重新渲染,它就会被计算
var app =new Vue({
    computed:{
        processText:()=>{} // processText作为返回值,反应到页面中。默认使用getter方法。
     fullName:{
         get:()=>{},
         set:(value)=>{} //app.fullName将调用该处方法
     }
    }
})
console.log(processText)

生命周期

var app = new Vue({
    created:()=>{} //实例创建完成后调用。阶段完成了数据的观测等,但尚未挂载,$el还不可用。需要初始化处理一些数据时会比较有用
    mounted:()=>{} //el挂载到实例上后调用,一般我们的第一个业务逻辑会在这里开始
    beforeDestroy:()=>{}//实例销毁之前调用
}

slot(插槽)

组件标签内部的内容称为插槽。该插槽定义在组件内容。插槽内容的获取可以使用slots对象。this.slots对象。this.slots.default

<child-component>
// slot
    <template scope="props"> // props变量,用于获取插槽定义的数据
        <p slot="指定内容在插槽显示的位置">父内容</p>
        <p>{{props.msg}}</p> // props.msg=子内容
    </template>
</child-component>

Vue.component('child-component',{
    template:'<div><slot msg="子内容"></slot>
    <slot name="指定内容在插槽显示的位置"></slot></div>'
})

vue-router用法

关于使用html5的history路由模式时,webpack-dev-server也需要配置支持该模式webpack-dev-server --open --history-api-fallback --config webpack.config.js

  1. 安装组件npm install --save vue-router

  2. 引入并使用import VueRouter from 'vue-router'; Vue.use(VueRouter)

  3. 路由方式

    • 静态路径
    const Routers =[{
        path:'/index',
        component:(resolve) => require(['./index.vue'],resolve)
        },{
        path:'/user/:id',// :id在user.vue可以使用$route.param.id获取值
        component:(resolve) => require(['./user.vue'],resolve)
        },
    ]
    const RouterConfig ={
        mode:'history'//使用html5 history路由模式
        routes:Routers
    }
    const router = new VueRouter(RouterConfig)
    new Vue({router:router})
    
    • 路由跳转
      • <router-link to="调转配置好的静态路由path" tag="渲染时的标签" >另外,repalce属性会导致无法使用后退;active-class属性会修改默认匹配路由后的class的名称
      • js代码
    this.$router.push('调转配置好的静态路由path') // 执行js时候的跳转
    this.$router.replace('调转配置好的静态路由path')// 无法使用后退
    this.$router.go(-1) //后退1页
    
    • 导航钩子 针对修改每一个跳转后的title,可以同一使用beforeEach钩子。在离开页面前执行。 跳转后的新页面可以使用afterEach钩子,使页面跳转到顶部。进入页面后执行。
    const router = new VueRouter(RouterConfig)
    router.beforeEach((to,from,next) =>{
        to 将进入目标的路由对象
        from 当前即将离开的路由对象
        next 进入下一个钩子
        tofrom对象可以通过meta获取信息
    })
    

vuex用法

核心变化是使用了观察者模式,将逻辑代码进行了拆分,同时将变量数据全局化。 安装、引入、启用vuex npm install --save vuex; import VueRouter from 'vuex'; Vue.use(Vuex);

const store = new Vuex.Store({
    state:{
        count:0list:[1,2,3,4,5]
    },
    // 同步方式,可以些执行逻辑
    mutations:{
        increment (state,可以扩展基础类型参数/对象){
            state.count++;
        }
    },
    // 获取变量get方法
    getters:{
        filterList:state=>{
            return state.list.filter(item=>item>2)
        },
        //该方法的默认参数有state对象变量本地,getter对象
        listCount:(state,getters)=>{
            return getters.filterList.length
        }
    },
    actions:{
    // context就是store对象
        incrementAsyn (context){
            context.commit('increment')
        }
    }
})
new Vue({store:store})
  • 在页面中可以直接使用{{$store.state.count}}获取值
  • 在页面中可以使用this.$store.commit('increment',传入increment的参数)increment为vuex中定义的逻辑方法
  • 在页面使用this.$store.getters.filterList获取过滤后的数组
  • 在页面actions块的调用方式是this.$store.dispath('incrementAsyn'),另外,可以actions块的逻辑块return promise对象,这样在调用的地方可以自定义回调响应

JavaScript的事件循环机制

调用同步js方法时:

  1. 生成运行环境(context,含有该方法的作用域、参数、this、引用)
  2. 发现内部方法,将其同上放入执行栈,返回结果后销毁,回到上一个运行环境 调用含有异步js方法时:
  3. 生成运行环境
  4. 发现有异步方法,将其放入一个队列中,并将结果放入一个队列中。主线程继续执行执行中的内容
  5. 当执行栈执行完毕后,再查找队列中的内容
  6. 取出排列第一位的事件,将其放入执行栈中 而vue异步更新dom的原理就是监听变量值的改变,将其值放入要给去重的队列中,等待下一个循环刷新队列并执行。$nextTick对象会立刻刷新队列(他的回调函数执行响应)

虚拟节点

  • 正常ul li渲染方式:创建一个ul节点,然后将字节点一个一个的渲染出来
  • 虚拟节点渲染方式:创建一个虚拟ul节点,再创建li的子节点,然后一次新渲染出来。

手动挂载实例

依靠Vue.extend和$mount两个方法挂载实例

var myComponent = Vue.extend({
    template:'<div>{{name}}</div>',
    data:()=>{
        retrun {name:'test'}
    }
})
new myComponent().$mount('#mount-div')

vue对jsx的支持

new Vue(
    el:'#app',
    render (){
        retrun (<h1>test<h1>)
    }
})

webpack基础

  • 重要也是必选的两项是入口(Entry)和出口(Output)。入口的作用是告诉webpack从哪里开始寻找依赖,并且编译,出口则用来配置编译后的文件存储位置和文件名。
  • output.path存放打包后文件的输出目录
  • output.publicPath指定资源文件引用目录
  • ooutput.filename指定输出文件名称
  • module对象的rules可以指定一系列加载器
  • module.test 正则表示式。编译过程中遇到的每一个import导入的css文件都用使用css-loader转换,然后style-loader转换
  • module.use 编译顺序是从后往前
  • plugins是一个定制的插件功能。
  • webpack就是一个js文件:webpack.config.js
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var config ={
    entry:{
        main:'./main'
    },
    output:{
        path:path.join(_driname,'./dist'),
        publicPath:'/dist/',
        filename:'main.js'
    },
    module:{
        rules:[{
            test:/\.css$/,
            use:['style-loader','css-loader']
        },{
        rules:[{
            test:/\.css$/,
            use:ExtractTextPlugin.extract({
                use:'css-loader',
                fallback:'style-loader'
            })
        }]
    },
    plugins:[new ExtractTextPlugin('mian.css')]
}
module.exports= config;

安装webpack与webpack-dev-server

npm install webpack --save-dev //开发版
npm install webpack-dev-server --save-dev //提供启动一个服务器、热更新、接口dialing等

安装CSS样式加载器

npm install css-loader --save-dev
npm install style-loader --save-dev

安装将所有css合并到一个css的插件

npm install extract-text-webpack-plugin --save-dev

启动webpack-dev-server服务脚本 webpack-dev-server --open --config webpack.config.js

生成环境编译命令 webpack --progress --hide-modules

webpack Vue Demo

  • 针对生成环境新建webpack.prod.config.js,同时增加"build":"webpack --progress --hide-modules --config webpack.prod.config.js"
  • 开发环境的scripts命令 "build":"webpack-dev-server --open --config webpack.config.js"
npm install --save vue
npm install --save-dev vue-loader
npm install --save-dev vue-style-loader
npm install --save-dev vue-template-compiler
npm install --save-dev vue-hot-reload-api
npm install --save-dev babel
npm install --save-dev babel-loader
npm install --save-dev babel-core
npm install --save-dev babel-plugin-transform-runtime
npm install --save-dev babel-preset-es2015
npm install --save-dev babel-runtime
npm install --save-dev url-loader // 文件支持
npm install --save-dev file-loader // 图片支持
npm install --save-dev webpack-merge //打包支持
npm install --save-dev html-webpack-plugin //打包支持

webpack.config.js
module.options是进一步对不同语言进行配置。因为vue的内容含有<template>、<script>、<style>,如果编译含有css就先使用css-loader加载,再使用vue-style-loader加

var path=require('path');
var ExtractTextPlugin=require('extract-text-webpack-plugin');
var config={
	entry:{
		main:'./main'
	},
	output:{
		path:path.join(_dirname,'./dist'),
		publicPath:'/dist/',
		filename:'main.js'
	},
	module:{
		rules:[{
			test:/\.vue$/,
			loader:'vue-1oader',
			options:{
				loaders:{
					css:ExtractTextPlugin.extract({
						use:'css-loader',
						fallback:'vue-style-loader'
						})
				}
			}
		},
                {
			test:/\.(gif|jpg|png|woff|svg|eot|ttf)\??.*$/,
			loader:'url-loader?limit=1024' //小于1kb 使用base64加载
		},
		{
			test:/\.js$/,
			loader:'babel-loader',
			exclude:/nodemodules/
		},
		{
			test:/\.css$/,
			use:ExtractTextPlugin.extract({
				use:'css-loader',
				fallback:'style-loader'
			})
		}]
	},
	plugins:[
		newExtractTextPlugin(”main.css”)
	]	
}
module.exports = config

webpack.prod.config.js
主是将开发环境的配置和生成环境进行合并,即prod是webpack.config.js的扩展

var webpack =require('webpack');
var HtmlwebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var merge = require('webpack-merge');
var webpackBaseConfig =require ('./webpack.config.js');

webpackBaseConfig.plugin=[]

module.exports=merge(webpackBaseConfig,{
	output:{
		publicPath:'/dist/',
		//将入口文件重命名为带有20位hash值的唯一文件,解决线上缓存的问题
		filename:'[name].[hash].js'
	},
	plugins:[
		new ExtractTextPlugin({
			filename : '[name].[hash].css',
			allChunks:true
		}),
		//定义当前node环境为生产环境
		new webpack.DefinePlugin({
			'process.env':{
				NODE_ENV:'"production"'
			}
		}),
		new webpack.optimize.UglifyJsPlugin({
			compress:{
				warings:false
			}
		}),
		//提取模板,保存入口html文件
		new HtmlwebpackPlugin({
			filename:'../index_prod.html',
			template:'./index.ejs', //是一个模板文件
			inject:false
		})
	]
})

//index.ejs
<!DOC TYPE html>
<html lang="zh-CN">
<head>
	<meta charset ="UTF-8">
	<title>webpack App</title>
<link rel="stylesheet" href="<%=htmlwebpackPlugin.files.css[0]%>">
</head>
<body>
    <div id ="app"></div>
    <script type="text/javascript" src="<%=htmlwebpackPlugin.files.js[0]%>"></script>
</body>
</html>

.babelrc
在webpack同级目录建立.babelrc文件,写入babel配置,webpack会依赖此配置文件来使用babel编译es6代码

{
    "presets":["es2015"],
    "plugins":["transorm-runtime"],
    "comments"false
}

Vue插件机制,可以全局添加一些功能

MyPlugin.install = function(Vue,options){
    //全局组件注册
    Vue.component('component-name',{})
    //添加实例方法
    Vue.prototype.$Notice = function(){}
    //添加全局方法或属性
    Vue.globalMethod = function(){}
    //添加全局混合
    Vue.mixin({mounted:function(){}})
}
//使用插件
vue.use(myPlugin)

ES6扫盲

data(){} 等同于 data:functoin(){}
h=>h('test') 等同于 function(h){return h('test')} 也等同于 h=>{return h('test')}