1. 基础
1.1 介绍
- Vue.js是什么? 它是构建数据驱动的web框架
- 为什么受欢迎?
- 声明式渲染, 应对前后端分离
- 渐进式框架,意味着你可以将vue作为你应用的一部分嵌入其中,带来更丰富的交互体验.
- 快速交付,结合三方UI框架
- Vue特点和高级功能
- 解耦视图和数据
- 可复用的组件
- 前端路由技术
- 状态管理vuex
- 虚拟DOM
- MVC MVP MVVM 架构模型对比
- "MVC": Controller 薄, View 厚, 业务逻辑大都部署在View
- "MVVM": 双向数据绑定,
- "MVP": View 薄,不部署任何逻辑,称为被动视图(Passive View) Presenter 厚 逻辑都部署于这里.
- 与React Angular 相似处:
- 都使用了虚拟DOM (Virtual DOM).
- 都提供了响应式(Reactive) 和组件化(Component)的视图组件.
- 都将注意力集中保持在核心库, 而将其他功能 (如 路由 全局状态管理) 交给其他相关库
- 与React Angular 不同处:
- vue 虚拟dom 轻量
- 学习成本低
- vue 有 css作用域
- React Native 能够渲染成原生的js,对应的vuex可以生成原生的js代码
- 多页面应用
- 缺点: 页面切换慢
- 优点: 首屏时间快, SEO优
- 单页面应用
- 优点: 页面切换快
- 缺点: 首屏时间慢, SEO差
- 引用方式? 引入CDN,下载js文件或者 搭建脚手架
- 如果有 template:
<div>你好</div>,渲染template中的代码,如果没有template渲染el接管的div. - el:既可以传一个字符串也可以传一个element,如document.querySelector('')
1.2 模板语法
- 插值:
- 文本 {{ name }} 或用 v-text="name", 把name的值原文输出到页面,先执行{{}}花括号中的,后执行v-text. 所以同时设置的时候,后执行的v-text 会把{{}}花括号中的覆盖
- 纯HTML
<p v-html="name"> </p>会把name变量中的值当作HTML 标签来渲染,这种转义输出 容易导致xss攻击 - 支持表达式运算
<p>{{ 10 + 3 - 2}} </p>页面输出11, 加减乘除,三目运算符都支持 - v-once 当花括号中的数据{{message}}发生变化时不会被再次渲染,只会按第一次的数据内容进行展示
- v-pre 相当于原生的pre标签, 标签里边的内容原封不动的显示出来,不会对相应的{{message}}数据进行解析渲染.
- v-cloak 在被el接管的div中加入 v-cloak这个属性, 并且在css文件中[v-cloak] {display:none}
- 常用指令:
- v-bind:href="website" 绑定属性 引号里边的内筒可以写表达式 三目运算符等等
- v-on:click="age++" 绑定事件,,其中age是属性
- v-on:click="addfun" 其中addfun是方法,若传参需要给小括号(), 如果给小括号 括号内没给参数,函数打印的是undefined. 如果括号内给了一个参数x,打印的是该参数. 如果没给小括号,则打印的是event. 如果想手动的获得到event则 addfun(123, $event)
- v-if增删 v-else-if, v-else, 加一个key值,防止dom复用
- v-show显隐, v-show不能和template一起使用也不能和v-else一起使用.
- 双向绑定
- v-model 双向数据绑定, 动态渲染和事件监听的结合体.
- v-model 结合radio类型使用
- v-model 结合checkbox使用
- v-model 结合select的使用
- v-model 值绑定
- v-model.lazy, 一旦有数据发生改变对应的data中的数据就会自动发生改变. 加上lazy修饰符之后data中的数据不会时时发生改变,只有在失去焦点或者回车时才会改变.
- v-model.number 默认情况下,无论输入框输入字母还是数字,都会被当做字符串类型进行处理.加上number修饰符之后可以让输入框内容自动转成数字类型.
- v-model.trim 加上trim修饰符可以过滤掉内容左右两边的空格
1.3 列表渲染
- 循环: v-for
<div v-for="(i, index) in list " :key="index"> {{ i }}--{{index}}</div> <div v-for="(i, index) in list " :key="i.id"> {{ i .text}}--{{index}}</div> 遍历对象时 v-for="(i,key,index) in list" - 数组循环, 当修改数组内容时, 不能通过下标的形式来改变数组,
通过数组的变异方法来修改数组才能够实现, 数据发生变化页面也跟着变,这种响应式的效果.- 七个: push pop shift unshift splice截取 sort排序 reserve逆序
- 改变引用, 直接操作vm.list的引用 让它指向另外一个地址空间
- set方法 Vue.set(vm.list, 1, 5)
- set方法 vm.$set(vm.list, 1, 5)
- 对象循环:
<div v-for="i in objList">{{ i }} </div> <div v-for="(i,key) in objList">{{ i }}--{{key}} </div> <div v-for="(i,key,index) in objList">{{ i }}--{{key}}--{{index}} </div> 对象和数组一样动态的用.key 赋值不会带来页面的变化, (1)用改引用的方式 添加数据页面更新 (好使) (2)set方法 Vue.set(vm.userInfo, "address", "shenzhen") (3)vm.$set((vm.userInfo, "address", "shenzhen") - v-for也可以遍历数字.
- 补充: template 站位符, 临时标签, 可以把循环体放在这里
1.4 计算属性
- 不想让插值语法{{}}花括号中的逻辑过多, 又想对原始数据属性进行某些格式的运算
- 所以可以在data中进行一些重复的数据定义,但是这样有会造成冗余
- 为此我们可以在computed中,定义计算属性,通过对原始data中的属性进行运算或者更改,就可以实现对原始数据进行格式化的操作
- 计算属性基于它的依赖可以进行缓存
- 计算属性只有它的相关依赖改变时才会重新计算
- methods 依赖属性变更和非依赖属性变更, 只要数据改变 每一次都会重新计算.
- 计算属性VS watch
- watch中的函数是不需要调用的, 不用return, 一个属性影响多个属性时用
- computed函数主动调用时不需加(), 需要return,多个属性影响一个属性时用, 计算属性不适用异步操作, 要用watch
- 计算属性getter setter
- 计算属性默认只有getter, 不过 在需要时你也可以提供一个setter
(1) computed: { fullName: function() {return this.firstName + this.lastName} } (2) computed: { fullName: { get: function() {return this.firstName + this.lastName} set: function(value) { var arr = value.split(" "); this.firstName = arr[0] this.lastName = arr[1] } } }
1.5 动态绑定 css样式和属性和 v-on
(1)动态绑定样式:
第1种
:class="{ activated: isact }"
在css文件中,定义activated的样式内容
在data中{ isact: true}
methods 中this.isact = !isact
第2种
:class="[ ac1 , ac2, ac3.....]"
在css文件中,定义activated1的样式内容
在data中{ ac1: "activated1"}
methods中 this.ac = "avtivated"
数组里边可以嵌套对象
第3种
:style="{fontSize: fSize +'px', color: '#45FFDD'}"
data中fSize: 40
第4种
:style="styleobj"
data中styleobj:{ color:"black"}
methods中 this.styleobj.color = this.styleobj.color ==="black"? "red":"balck"
第5种
:style="[ styleobj, { fontSize:'10px' } ]"
data中styleobj:{ color:"black"}
methods中 this.styleobj.color = this.styleobj.color ==="black"? "red":"balck"
(2)动态绑定属性:
<div :[myname]="value"></div> 其中myname和value是变量
如果我们希望将一个对象的所有属性,绑定到dom上,使之成为dom元素的所有属性:
<div v-bind="myinfo"></div> 其中myinfo是变量
<div :="myinfo"></div> 其中myinfo是变量
(3) v-on的使用
缩写 @
参数event
修饰符:
.stop 调佣event.stopPropagation() 阻止冒泡
.prevent 调用preventDefault() 阻止默认行为
.capture 添加事件监听器时使用capture模式
.self 只当事件是从监听器绑定的元素本身触发时 才触发回调
.once 只触发一次
.left 只当点击鼠标左键时触发
.right 只当点击鼠标右键时触发
.middle 只当点击鼠标中建时触发
.passive {passive: true} 模式添加侦听器
.native 监听组件根元素的原生事件
.keyup.enter监听键盘按键 用.enter来监听具体哪个按键
1.6 生命周期钩子
- 生命周期函数就是vue实例在某个时间点会自动执行的函数
- new Vue() 创建vue实例,系统会初始化事件和生命周期函数 init Events & Lifecycle
- beforeCreate() 组件创建之前, vue会进行外部注入 和 双向绑定相关的内容 init injection & reactivity
- created() 组件创建后,该函数会自动执行.
(1)Has ‘el’ option ? 是否有el这个选项
有, 走yes这条线, 直接进入下一步
没有, 走no这条线, when vm.$mount(el) is called
(2)Has ‘template’ option ? 是否有 template这个选项
yes, Complle template into render function 用模板去渲染
no, Complle els outerHTML as template 把el外层的标签当成模板
-
beforeMount() 挂载之前, 模板和数据相结合的DOM元素,挂载到页面上
-
mounted() 挂载完毕,自动执行该函数
-
beforeUpdate() 在挂载的情况下, 当数据改变 还没重新渲染之前,该函数自动执行
-
Updated 重新渲染之后, 该函数自动执行
-
beforeDestroy() 在组件销毁之前会调用 vm.$destroy()
-
Destroyed() 在组件销毁之后会调用
-
activated
-
deactivated
-
errorCaptured
1.7 相关细节
- 可以实例化多个vue实例, 分别接管不同的div 标签
- tr封装成子组件, table中是tbody,tbody中是tr. 页面用子组件名调用子组件,会出现结构上的错误.
将<row></row> 改写成<tr is="row"> </tr>. 同样ul,ol,select这样的标签也是一样的 - 子组件的data 定义时必须是一个函数,不能像根组件一样是一个对象. 同时这个函数return一个对象,这个对象中包含相应的数据. 之所以这样是因为不同的地方调用这个子组件的时候,会有自己不同的数据.而根组件转只会被调用一次,子组件会被调用多次.
- 属性ref="name1" 是用来标记标签的,当使用时 this.$refs.name1 获得当前Dom节点
1.8 自定义指令
(1) 新建一个js文件
export default {
bind(el, binding){ // 钩子函数, 指令绑定
},
inserted(el, binding) { // 钩子函数, dom插入到父级元素
const roles = store.state.userInfo.roles
if(!roles.includes(binding.value)) {
el.parentNode.removeChild(el)
}
}
}
(2)
在使用的文件.vue中 import hasRole from './xxx/role.js'
directives: {
hasRole
}
<button v-hasRole="'admin'"> </button>
2.组件
2.1 基础
- 组件定义和使用
(1) 创建组件构造器对象
const navC = Vue.extend({
template:'
<div>
<h2>标题</2>
<p> 这是内容</p>
<button>按钮</button>
</div>
'
})
(2) 注册组件
Vue.component(' 使用时的标签名 my-nav',' 组件构造器 navC ')
(3) 使用组件: 在html中 <my-nav> </my-nav> 来使用这个组件
- 全局组件
(1) Vue.component('标签名','组件构造器')
全局组件可以在多个vue实例中使用
(2)vue2.x 版本注册全局组件
Vue.component('标签名','{
template:'
<div>
<h2>标题</2>
<p> 这是内容</p>
<button>按钮</button>
</div>
'
}')
这种方式,内部已经帮你extend()
- 局部组件
(1) const navC = Vue.extend({template: ...........})
在vue实例中用components: { nav-c: navC }
(2 )vue2.x 版本注册局部组件
const navC = {
template:'
<div>
<h2>标题</2>
<p> 这是内容</p>
<button>按钮</button>
</div>
'
}
在vue实例中用components: { nav-c: navC }
2.2 组件模板抽离
<template id=nav3>
<div>
<h2>标题</2>
<p> 这是内容</p>
<button>按钮</button>
</div>
</template>
或者是如下写法
<script type="text/x-template" id="nav3">
<div>
<h2>标题</2>
<p> 这是内容</p>
<button>按钮</button>
</div>
</script>
最后注册组件:
Vue.component('nav', {
template: '#nav3'
})
2.3 父组件给子组件传数据
-
页面通过绑定属性的方式向子组件传值,子组件中用props接收.
-
绑定时不支持驼峰命名
-
不要在子组件中修改父组件传来的数据,保证单向数据流.因为当页面传过来的数据不是原始值,是引用值的时候,子组件修改props中引用类型的数据,也会影响到其他子组件数据的值.
-
子组件对页面传来的数据,进行参数校验.
(1)props: { content: String } 此时页面要是传来的不是字符串类型就会报错.
(2)props: { content: [String,Number] }
(3)props: { content: {
type: String,
required: true,
default: '这是默认值'
}}
(4)props: {
type: String,
validator: function(value) {
return (value.length > 5)
}
}
(5)非props特性,父组件向子组件传递了一个数据,而子组件并没有接收这个数据,这时候在子组件使用这个数据就会报错.
父组件向子组件传递一个数据,若子组件接收了,这个属性就不会出现在子组件中.若是子组件没有接收,这个属性就会出现在子组件的属性当中.
2.4 父组件给子组件传css
2.5 父组件给子组件传 html结构标签
- 页面中给子组件传递标签,子组件template中用slot来接收, 如果父组件不传,子组件的slot可以有自己的默认值.
- 不同的位置插入不同的内容,可以使用具名插槽slot="theName"
- 作用域插槽??? 页面使用插槽给子组件传标签,但是内容由子组件来决定.
2.6 子组件给父组件传事件
- 子组件methods内事件函数中 this.$emit('click') 向外触发事件,父组件中@click="handleChange",来接收这个事件.
- 如果父组件中@click.native="handleClick", 这样接收的就是原生的事件.
2.7 非父子组件的传值
- 利用bus总线机制解决非父子组件传值 ???
Vue.prototype.bus = new Vue()
子组件的事件处理函数中 this.bus.$emit('change', this.content)
在子组件mounted中 this.bus.$on('change',function(value) { })
2.8 父子组件的相互访问
- 父组件访问子组件: 使用.refs 相当于用对象key值的形式获得子组件.
- 子组件访问父组件: 使用.$parent
2.9 动态组件和v-noce
(1) <component :is="type"> </component> 他会根据is中数据的变化,自动加载不同的组件.这就是动态组件
(2) 以上在组件的切换过程中,会销毁原来的组件,重新创建一个新的组件. 这样会耗费性能
(3)在子组件的template中,根div标签加v-once属性,当子组件被切换显示的时候,会直接的从内存中读取,提高性能
3. 组件动画
3.1 transition动画
<transition name="fade3"> <div>需要添加动画的标签</div> </transition>
- .fade3-enter ???
- .fade3-enter-active ???
- .fade3-enter-to ???
- .fade3-leave ???
- .fade3-leave-active ???
- .fade3-leave-to ???
3.2 Animate.css库
- ???
4. 模块化开发
4.1 闭包和模块
- 匿名函数(闭包)虽然解决了之前多个js文件命名冲突的问题.但是代码复用率低
- 所以我们可以把将要暴露到外边的变量,使用一个模块 导出去.
- 不同的模块的可以单独封装自己的属性和方法.
- 这就是模块最基础的封装,事实上模块的封装还有很多高级的话题
- 常见的模块化规范:CommonJS, AMD, CMD, 也有ES6的Modules
4.2 CommonJS
- 模块化有两个核心: 导出和导入, webpack用的是Commonjs
(1) CommonJS的导出
flag: true,
var name = 'lisi'
var age = 18
test(a,b) {
return a+b
},
demo(a,b) {
return a*b
}
module.exports = {
test,
demo,
name,
age
}
(2) CommonJS的导入
let { test,age,name } = require('./moduleA.js')
// 等同于
let modA = require('moduleA')
let test = modA.test
let age = modA.age
let name = modA.name
4.3 Es6新增两个关键字 export(导出)/import(导入)
-
导出方式一: export { name,age }
-
导出方式二: export var num=30;
-
export 除了导出变量和对象, 也可以导出函数 和导出类,
-
导入步骤一: 在HTML代码中引入两个js文件
<script src="aaK.js" type='module'> </script> -
导入步骤二: import { name,age } from "./aaK.js"
-
export default xxx :某些情况下,一个模块中包含某个功能,我们并不希望给这个功能命名, 而且让导入者自己来命名. 这时用export default来导出模块里边想传出来的内容.
-
在同一模块中 不允许同时纯在多个export default xxx
-
import * as a9 from "aak.js", 使用时a9.name a9.age
5. webpack
5.1 认识
- 从本质上来讲,webpack是一个现代的JavaScript应用的静态模块打包工具
- grunt/gulp也可以用来打包, grunt基本没人用了. 对于gulp,我们可以配置一系列的task任务流,并且定义task要处理的事务(例如es6,ts转化,图片压缩,sass转成css),之后让grunt/gulp来一次执行这些task,而且让整个流程自动化. 所以grunt/gulp也被称为前端自动化任务管理工具.
- 什么时候用grunt/gulp?
- 如果你的工程模块依赖非常简单,甚至是没有用到模块化的概念.
- 只需要进行简单的合并,压缩,就使用grunt/gulp 即可
- 但是如果整个项目使用了模块化管理,而且相互依赖非常强,我们就可以使用更加强大的webpack了
- 区别: grunt/gulp更加强调的是前端流程的自动化,模块化不是它的核心. webpack更加强调模块化开发管理,而文件压缩合并,预处理等功能是他附带的功能. webpack各种模块化都支持(commonJS,es6等等)所以它的功能更加强大.
5.2 webpack安装
- webpack依赖于node环境,node环境为了可以正常执行必须依赖很多包. 所以要安装一个工具 npm工具(node packages manager). npm这个工具只是为了 帮助我们管理node环境下边的各种包而已.
- 全局安装: npm install webpack -g
- 局部安装: npm install webpack --save-dev 是开发时的依赖, 项目打包后不需要继续使用的依赖. 所以不用打包到包里. 运行依赖,是项目能正常运行的依赖,需要打包到包里
- package.json 这里边定义一些脚本script, 这个脚本里用的就可以是局部的
5.3 webpack的使用
- src文件夹,放的是源码
- dist文件夹,放的是打包之后的内容. [distribution发布]
- 打包: webpack ./src/main.js ./dist/bundle.js
- 在页面引入 bundle.js
5.4 webpack的配置
- 不用每一次改变发生更新都打包一次webpack ./src/main.js ./dist/bundle.js 我们可以在webpack.config.js文件里边进行配置:
(1) 在node里获取动态路径, 首先npm init 来初始化,回车之后, 起包名,写版本号--->会生成package.json
(2) npm install 如果package中有依赖的东西,会安装一些相应的依赖
const path = require('path') //获取动态路径
module.exports = {
entry: './src/main.js',
output: {
// resolve是node封装的内置函数,__dirname也是node里边的用于获得绝对对路径
path: path.resolve(__dirname,'dist')
filename: 'bundle.js'
}
}
- 通过commonjs方式导出, 在终端执行 webpack就可以了.
- 让webpack根 npm run build映射起来,在package.json中 scripts:{'build': "webpack"}
5.5 loader
- loader是webpack中一个非常核心的概念,在main.js中 require('xx.css')
- webpack主要来处理我们写的js代码,并且webpack会自动处理js之间相关的依赖. 但是在开发中我们不仅仅有基本的js代码处理,我们也需要加载css 图片 也包括一些高级的将ES6转成ES5代码,将TypeScript转成ES5代码,将scss less转成css,将.jsx .vue文件转成js文件等等.
- 对于webpack本身的能力来说,这些转化是不支持的.
- 所以我们给webpack扩展对应的loader就可以啦.
- 使用过程
- npm install --save-dev css-loader
- webpack.config.js中
module关键字下rules: [test: /\.css$/ , use:['css-loader']
图片用url-loader 当图片大小大于limit时用 file-loader,在webpack.config.js文件中,output: {publicPath: 'dist/'}, publicPath这个属性的目的就是在url前边加上一个对应的路径
5.6 ES6转成ES5
- 安装 npm install --save-dev babel-loader@7 babel-core babel-preset-es2015
- 在webpack.config.js中module关键字下rules: 配置
6. 在webpack里边配置Vue
6.1 安装vue
- 运行时依赖 npm install vue --save
- main.js中 import Vue from 'vue'
打包vue时有 runtime-only和runtime-compiler两个版本
runtime-only 代码中 不可以有任何的template
runtime-compiler 代码中可以有template, 因为有compiler可以用于编译template
解决这个问题,在webpack.config.js中
resolve: {
extension: ['.js','.css','.vue'], // 省略扩展名
alias: { // 别名
'vue$': 'vue/dist/vue.esm.js'
}
}
- 单页面复应用SPA ( simple page web application )
- el和template之间的关系
- 安装:npm install vue-loader vue-template-compiler save-dev
6.2 plugin
- plugin是插件的意思, 通常对现有的框架进行扩充扩展的.
- webpack中的插件,就是对webpack现有的功能进行扩充,比如打包优化,文件压缩等等.
- loader和plugin的区别:
- loader主要用于转换某些类型的模块,它是一个转换器.
- plugin是插件,它是对webpack本身的扩展
- plugin的安装
- npm install plugin
- 在webpack.config.js中配置plugins
- 哪些插件可以扩充webpack:
1.添加版权的plugin
(1)我们先来使用一个最简单的插件,为打包的文件添加版权声明,这个是webpack自带的插件.
(2)在webpack.config.js文件中
const path = require('path')
const webpack = require('webpack')
module.exports = {
plugins: [
new webpack.BannerPlugin('最终版权归')
]
}
2. 打包html的插件
(1)安装: npm install html-webpack-plugin --save-dev
(2)配置webpack.config.js 中
const HtmWebpackPlugin = require('html-webpack-plugin')
plugins: [
new webpack.BannerPlugin('最终版权归'),
new HtmlWebpackPlugin({
template: 'index.html'
})
]
3.对js进行压缩的插件
(1)安装: npm install uglifyjs-webpack-plugin@1.1.1 --save-dev
(2)配置webpack.config.js 中
const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')
plugins: [
new webpack.BannerPlugin('最终版权归'),
new HtmlWebpackPlugin({
template: 'index.html'
})
]
6.3 搭建本地服务器
- 搭建本地开发服务器,这个服务器是基于node.js的,内部使用express框架,可以实现我们想要的让浏览器自动更新显示我们修改后的结果.
- 不过它是一个单独的模块,在webpack中使用之前需要先安装它.
- 把我们未来要打包生成的东西,先暂时放到内存里边, 以后npm run build可能只需要执行一次就可以了.输入npm run build才会将dish文件存放到磁盘里边.
- npm install --save-dev webpack-dev-server@2.9.1 提供一个开发时的服务的.
- 在webpack.config.js下配置
devServer: {
contentBase: '/dist', //要服务的文件夹
inline: true, // 是否需要实时监听
port: 默认是8080
historyApiFallback: 在SPA页面中,依赖H5的history模式
}
package.json文件中scripts下 'dev': "webpack-dev-server --open"
终端: npm run dev 来运行这个本地服务器
6.4 webpack.config.js文件分离
- 新建base.config.js文件, 公共的东西放到这里
- 新建dev.config.js文件, 开发依赖放到这里
- 新建prod.config.js文件, 生产的东西放到这里
- 把webpack.config.js文件的内容 按类别复制到以上三个文件里边
- 开发编译时只需要base.config.js和dev.config.js, 打包时只需要base.config.js和prod.config.js
- 安装合并包: npm install webpack-merge --save-dev
- 在prod.config.js文件中:
const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')
const webpackMerge = require('webpack-merge')
const baseConfig = require('./base.config')
module.exports = webpackMerge(baseConfig,{
plugins: [
new UglifyjsWebpackPlugin()
]
})
在package.json文件中:
"build": "webpack --config ./build/prod.config.js"
"dev": "webpack-dev-server --open --config ./build/dev.config.js"
在base.config.js文件中, output: { path: resolve(__dirname, '../dist') }
6.5 安装vue脚手架CLI
- CLI是 Command-Line Interface,翻译为命令行界面,俗称脚手架
- 安装脚手架( 基于nodejs环境和webpack )
- npm install -g @vue/cli
- yarn global add @vue/cli
- 卸载 npm uninstall -g @vue/cli
- 卸载 yarn global remove @vue/cli
- 更新 npm i -g @vue/cli
- vue --version 来检测是否安装成功
- 也可以先安装淘宝镜像,然后用cnpm代替npm来进行模块的安装:
npm install -g cnpm --registry=https:/registry.npm.taobao.org - 配置淘宝镜像: npm config set registry registry.npm.taobao.org
- runtime-compiler和runtime-only的区别
new Vue({
el: "#app",
template: '<App/>',
components: {App}
})
new Vue({
el: '#app',
render: h => h(App)
})
runtime-compiler: template 解析成 ast(abstractTree) 编译成 render 生成 Vdom 渲染成 UI
runtime-only: render 生成 Vdom 渲染成 UI [性能更高,]
h是形参,实参是函数createElement
createElement('标签',{标签的属性},['标签的内容'])
例子: return createElement('h2',{class: 'box'},['你好' , 'hello'])
- 脚手架3 创建项目: vue create vue-project
(1) 创建项目的 各个选项: *Babel TypeScript *Progressive Web App (PWA) Support 先进: 缓存很多东西,推送通知 *Router *Vuex *CSS pre-processors Linter / Formatter Unit Testing E2E Testing Use class-style component syntax? (Y/n) 是否使用class风格的组语法 (2) *In dedicated config files In package.json (3) - 运行项目
- cd到 项目目录下
- npm init 和 npm install
- npm run serve
- 打包 npm run build
7.vue-router的相关技术
路由: 就是通过互联的网络把信息从原地址传输到目的地址的活动
7.1 第一阶段
- 后端路由: 后端处理URL和页面之间的映射关系
- 后端渲染: 后端通过jsp等技术,从数据库读取数据,并且将它动态的放在页面中.服务器传到客户端的是html和css
7.2 第二阶段
- 前后端分离阶段: 后端只负责数据,不负责任何界面的内容. 前端通过Ajax获取数据. 目前很多网站依然采用这种模式
- 前端渲染: 浏览器中显示的网页大部分内容都是由前端写的js代码在浏览器中执行, 最终渲染出来的网页.
7.3 第三阶段
- 单页面富应用阶段: SPA最主要的特点就是在前后端分离的基础上加了一层前端路由.
7.4 vue-router前端路由
- 前端路由的核心是什么? 在改变URL时, 页面不进行整体的刷新. 怎么实现这个功能?
方法一: 改变url上边的哈希值, location.hash = 'foo'
如果改变href会刷新, location.href = 'aaa'
方法二: 通过history的改变, history.pushState({},'',home)
存储路由 路径的是一个栈结构,先进后出,浏览器显示的路径是栈顶, 也就是最后一个被压入栈的路径.
history.back() 执行这个移除原来的栈顶,第二个成为新的栈顶,浏览器路径会展示这个新的栈顶 back() 方法相当于go(-1) forward()相当于go(1)
history.replaceState({},'',home) 这个是替换当前路径,是不能返回的
7.5 安装和使用vue-router
- npm install vue-router --save
- 在模块化中使用
- 导入VueRouter
- Vue.use(VueRouter)
- 创建路由实例对象,const router = new VueRouter({ routes })
- 导出对象
- main.js引入并且挂载
- router-link相当于a标签, router-view是站位的, keep-alive是从缓存区取数据.
- redirect 重定向, 设置默认路径
7.6 router-link的属性值
- to属性 相当于 a标签的href
- 不想默认渲染成a标签了, 通过tag="button"属性来设置,
- replace 跳转不可返回
- 在css中通过 .router-link-active 给活跃的router-link设置单独的样式. 若要自己改变这个类名:
- (1)可通过active-class='active2'属性来 指定自定义的类名
- (2)在router目录下index.js文件中, 创建路由对象时添加linkActiveClass: 'active2'
- 不用router-link组件来渲染, 通过代码修改路由的路径
this.$router.push('/home')
this.$router.replace('/home')
7.7 动态路由
(1) 在router.js 文件中
{
path: '/user/:userid',
component: User
}
(1) 首页 (--->跳用户页)
<router-link :to=" '/user/' + userid "></router-link>
data() {
return {
userid:'123'
}
}
(2) 用户页
computed: { // 当前哪个路由处于活跃状态,this.$route就是哪个对象.
userId() {
return this.$route.params.userid
}
}
7.8 路由懒加载
- component: () => import('components/Home')
7.9 路由嵌套
- children: 该数组中的内容各式 与routes一样即可
7.10 路由传参数
- query的方式: 大量的数据用query
(1) 组件跳转
<router-link :to="{path: '/user', query: {name: 'zhang', age: 19, height: 170} }">
用户
</router-link>
(2) 代码跳转
methods中 this.$router.push('/user/' + this.userid ) 改成对象写法
this.$router.push({
path: '/user',
query: {
name: 'zhang',
age: 19
}
})
目标页面取值{{$route.query.name}}
7.11 route的区别
- $router 是new出来的路由对象
- $route 是当前正在访问 处于活跃状态的那个路由
7.12 导航守卫
(1)全局守卫
// 前置守卫
router.beforeEach((to, from, next) => {
document.title = to.matched[0].meta.title // 有路由嵌套要用matched
// 手动调
next()
})
// 后置守卫
router.afterEach((to, from) => {
})
(2)路由独享守卫
routes: [
{
path: '/user',
component: User,
beforeEnter:(to, from, next) => {
// 进入到这个路由就会调用
}
},
{},
............
]
(3)组件内守卫
// 进入之前
beforeRouteEnter(to, from, next) {
// 在组件渲染前调用, 里边不能用this
}
// 路由改变,但改组件被复用
beforeRouteUpdate (to, from, next) {
// 例如 /user/:id, 在/user/1 和 /user/2之间进行跳转时触发,, 可以用this
}
beforeRouteLeave (to, from, next) {
// 离开之前调用, 可以用this
}
7.13 keep-alive
- router-view 是一个组件, 如果直接用keep-alive,所有路径匹配到的视图组件都会被缓存
- 用了keep-alive包裹的组件,页面切换时, destroy钩子将不会触发, 因为改组件没有销毁
- 但是多了两个勾子, activated 和 deactivated 在活跃和不活跃之前切换
- include 只有匹配的组件会被缓存
- exclude 任何匹配的组件都不会被缓存
8. promise
promise是异步编程的一种解决方案
8.1
- new Promise(参数), 参数本书就是一个函数
- new Promise( (resolve, reject) => { resolve() 一旦你在这个位置调用resolve,他就会在最后边调用一个.then()})
8.2 promise三种状态
-
首先当我们开发中有异步操作时, 就可以给异步操作包装一个Promise
-
pending: 等待状态,正在进行网络请求或者定时器没到时间等等
-
fulfill: 满足状态,当我们主动回调了resolve时,就处于该状态,并且会回调.then()
-
reject: 拒绝状态,当我们主动回调了reject时,就处于该状态,并且会回调.catch()
-
.then(函数1,函数2) 函数2是当reject时,自动执行函数2
-
.then(函数1).catch(函数2) 这样也可以执行函数2
-
then中 可以继续返回一个resolve
(1)
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('aaa')
}, 1000)
}).then(res => {
// 1.这里有100行自己的业务代码
// 2.对结果进行处理
return new Promise((resolve, reject) =>{
resolve(res + "567")
})
})
(2)
上边的第2步可以简写成 return Promise.resolve(res + '567')
也可以简写成 return res + '567'
链式调用过程中, 各个.then中出现了异常,都会在.catch中捕获到, 手动抛出的异常throw也能在catch中捕获到
(3)
Promise.all([网络请求1, 网络请求2]).then(results => {
results是一个数组,, 里边包含了第一个请求的结果,, 第二个请求的结果
})
9. Vuex
9.1 Vuex是做什么的
- Vuex是一个专为Vue.js应用 开发的状态管理模式.
- 它采用 集中式存储管理 应用的所有组件的状态, 并以相应的规则保证状态以一种可预测的方式发生变化.
- Vuex 也集成到Vue的官方调试工具devtools extension,提供了诸如零配置的time-travel调试,状态快照导入导出等高级调试功能.
9.2 状态管理是什么
- 状态管理模式,集中式存储管理: 非父子组件公共的状态值,就是公共变量 全部存储在一个对象里. 多个组件可以共享这个对象里边的变量.
- 这个对象可以自己封装,但是自己封装做不到一处变处处变的响应式.
- 官方已经封装好了 这个对象,就是Vuex
9.3 什么情况需要用到状态管理
- 用户登录状态, 用户名称,头像,地理位置
- 商品的收藏,购物车的物品
9.4 vuex单界面状态管理到多界面状态管理
- 单页面状态管理
- 多页面状态管理
(1) 不要直接修改state里边的值, 直接改造成浏览器插件Devtools监听不到
(2) 要通过mutations来进行修改
(3) 若有一步操作则 通过Actions再通过mutations来修改
9.5 vuex中state
- 用来定义公共的状态值
- 在组件中用 $store.state.age 来取值
- vuex提出使用单一状态树,什么是单一状态树? Single Source of Truth 可以翻译成单一数据源.
9.6 Getters 相当于组件中的计算属性
- 在getters中,自定义一个方法对state中数据进行一些格式化处理等等操作.
- 在组件中 $store.getters.myPower()
- gettes中的第一个参数默认是state,第二个参数是getters本身
- getters中自定义的方法,可以返回一个函数, 使用处自己给函数传参数
9.7 vuex中mutations
(1)
第一个参数默认是state,第二个参数是使用出传过来的参数
在mutations中自定义方法
increment(state) {state.age++}
increment(state) {state.age--}
(2)
在使用的组件中: 普通封装 this.$store.commit('increment', name) 来改变值
在使用的组件中: 特殊封装 this.$store.commit({
type: 'increment',
name: name
}) 来改变值
(3)vuex响应式原理??
(4)类型常量??
9.8 Action 有异步操作放在这里
- action类似于mutations, 但是它可以代替mutation来进行异步操作
- action默认的第一个参数是context, context相当于store
- context.commit('increment',name) 其中increment是定义在mutations中的方法
- 在使用的组件中 this.$store.dispatch('update') 其中update是自定义在actions中的方法
- 传参??
9.9 module模块化??
9.10 store的目录组织??
10. 网络请求(axios)
10.1 Vue中发送网络请求方式非常多
- 选择一: 传统的ajax是基于XMLHttpRequest(XHR)
- 选择二: jQuery-Ajax
- 选择三: jsonp
- 选择四: axios
10.2 axios
- 功能特点
- 在浏览器中发送XMLHttpRequests请求
- 可以在node.js中发送http请求
- 支持PromiseApi
- 请求拦截和响应
- 转换请求和响应数据等等
- 支持多种请求方式
axios(config)
axios.request(config)
axios.get(config)
axios.delete(url [,config])
axios.head(url [,config])
axios.post(url [,data[,config]])
axios.put(url [,data[,config]])
axios.patch(url [,data[,config]])
- axios请求细节
(1)get请求
get 请求数据用 params: {} 传参数
(2)并发请求
axios.all([ axios(), axios() ]).then(results=>{
conlsole.log(results)
})
axios.all([ axios(), axios() ]).then(axios.spread((res1, res2=>{
console.log(res1)
console.log(res2)
}))
all方法是所有请求成功之后在往下进行
使用axios.spread可以将返回回来的数组结果 进行展开
- axios 全局配置
axios.defaults.baseURL = "123.123.123.23.:8000"
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'
常见的配置项 如下:
method 请求方式
baseURL: 'http://www.xxx.com/api'请求根路径
transformRequest: [function(data) {}] 请求前的数据处理 (生命周期函数)
transformResponse: [function(data) {}] 请求后的数据处理 (生命周期函数)
headers: {'x-Requested-With': 'XMLHttpRequest'},
params: {id:12} get查询参数对象
data: {key: 'aa'} post 传参数的请求体
paramsSerializer: function(params){ } 查询对象序列化函数
timeout: 1000, 超时时间
withCredentials: false 跨域是否带Token
adapter: function(resolve,reject,config){} 自定义请求处理
auth: {uname: '', pwd: '12'} 身份验证信息
responseType: 'json' 响应后的数据格式
10.3 axios实例
- 如果不封装, 默认使用全局的axios 和 全局的配置发送网络请求
- const instance1 = axios.create( {} ) 来创建实例1
- const instance2 = axios.create( {} ) 来创建实例2
import axios from 'axios'
import https from 'https'
export function myaxios(config) {
// 1 创建axios的实例
const instance = axios.create({
// baseURL: urlDns,
timeout: 5000,
// httpsAgent: new https.Agent({ // 取消验证ssl证书
// rejectUnauthorized: false
// }),
headers: {
'Content-Type':'application/x-www-form-urlencoded', // 设置传输的数据格式text/html application/x-www-form-urlencoded application/json
}
})
// 2.1 axios的拦截器,请求时的拦截 (发请求之前的 生命周期函数)
instance.interceptors.request.use(config => {
console.log("请求成功时: 触发的拦截")
// 1. 比如config中的一些信息不符合服务器的要求
// 2. 比如每次发送网络请求时, 都希望在界面中显示出一个请求的图标
// 3. 某些网络请求,必须携带一些特殊的信息. (如登录要token 否则让他跳到登录页面)
return config
},err => {
console.log("请求失败失败时: 触发的拦截")
console.log(err)
})
// 2.2 响应时的拦截 (数据返回之前的 生命周期函数)
instance.interceptors.response.use(res => {
console.log("响应成功时: 触发的拦截")
console.log(res)
return res
}, err => {
console.log("响应失败时: 触发的拦截")
console.log(err)
})
// 3 发送真正的网络请求
return instance(config) // 返回是一个promise对象
}
12.插件
12.1 console插件
- npm install babel-plugin-transform-remove-console --save-dev
- 在babel.config.js中 plugins数组中 增加'transform-remove-console'
- process.env.NODE_ENV === 'production' 这是生产环境