作者:尤雨溪
渐进式框架:根据自身需求,选择框架的中的工具,VUE不强求你一次性接受并使用它的全部功能特性
Vue
数据驱动
渐进式框架
基于MVVM软件设计模式
React
开发大型项目最严谨的框架
Anagle
适用于大型项目,项目开发较为沉重,不够灵活
Vue与jquery的区别(打扫卫生)
原生JS
扫把、簸箕、拖把
Jquery
戴森吸尘器
Vue
扫地机器人(保姆)
2.MVVM(软件设计模式)
Vue.js根据这种模式设计出来
M: model数据模型
V:view 视图层
VM:View-Model视图模型
数据模型发生修改=》通知VM=》通知View视图做出响应的更新
View更改后=》通知VM=》通知Model数据模型,更新数据
3.安装
4.生命周期
vue实例从创建到挂载到更新,最后销毁,这整个流程叫做vue的生命周期
生命周期钩子函数:方便去操作当前阶段的vue实例
// 初始化构建阶段
beforeCreate vue实例初始化完成之前,完成了vue事件、属性的初始化,但是访问不到vue实例中的data、methods
created vue实例初始化完成,可以访问实例内部的数据和方法
// 挂载阶段
beforeMount 完成了模板的解析,但是数据没有绑定到模板上
mounted vm.$el虚拟dom替换el Dom,完成了数据绑定/dom树渲染。
// 更新阶段
beforeUpdate 数据以及修改,虚拟dom也构建完毕,但是没有渲染到页面上
updated 更新过后的虚拟dom节点,成功渲染到页面上
// 销毁阶段 this.$destroy()
beforeDestroy vue实例销毁之前,还可以访问实例
destroyed vue实例上绑定的事件、监听器、子组件销毁完毕,访问不到vue实例了
5.模板语法
文本插值
{{msg}} 用双大括号将data数据模型中的字段渲染到页面中
{{ msg + ‘Nihao’ }} 双大括号内部可以放js表达式
指令
v-bind:动态绑定元素上的属性
v-bind:title 简写=> :title=“title”
v-html:绑定html代码片段
富文本编辑器
有格式的内容 => 相应html代码片段
v-on:动态绑定事件
v-on:click=“clickHandler”
简写 => @click=“clickHandler”
条件渲染
v-if v- else
v-show
区别:
v-if v-else 通过控制dom节点的渲染,实现显示与隐藏
v-show 通过控制dom节点css样式中display,来实现显示与隐藏
频繁的显示与隐藏某个组件时,用v-show较好
v-if会频繁渲染dom树,占用资源,影响代码运行效率
列表渲染
v-for
使用:
- {{item}}{{index}}
class绑定
:class
块级元素
块级元素
块级元素
style绑定
:style
块级元素
块级元素
块级元素
1.事件机制
事件传参
@click=“handler(‘1’,$event)”
handler(id,event) {}
事件修饰符
.stop 停止事件冒泡
.prevent 阻止事件默认行为
.capture 在事件捕获阶段执行事件处理函数
.self 只当在 event.target 是当前元素自身时触发处理函数
.once 事件处理函数执行一次后解绑
.passive 滚动事件的默认行为 (即滚动行为) 将会立即触发 ,一般与scroll连用,能够提升移动端的性能
按键修饰符
.enter、.tab、.delete、.esc、.space、.up、.down、.left、.right
.ctrl、.alt、.shift、.meta
鼠标修饰符
.left、.right、.middle
2.表单
双向数据绑定
通过v-model这条指令
视图修改 => viewModel => model修改数据
model数据改变 => viewModel => view视图更新
修饰符
.lazy v-model由原来的input触发转成change事件触发
.number 将当前输入框的输出值自动转成number类型
.trim 将输出值前后的空格消除掉
表单控件
3.计算属性
可以将某个变量经过计算然后再输出
1.概述 我是组件
组件可以拓展HTML元素,内部封装了可复用的HTML、CSS、JS代码片段。
组件类似于vue实例,组件内部有template属性,用于指定模板,
vue实例内部有el属性,用于指定模板
2.组件定义
let component = {
data() {
return {
// 返回对象值的唯一拷贝,保证复用的组件内部数据不会相互影响
msg: ‘我是组件的数据’
}
},
template: {{msg}}
}
3.组件注册
全局注册的组件,可以在任意vue实例或者组件内部使用
局部注册的组件,只能在当前注册的实例或者组件内部使用
类似于js中的全局变量、局部变量
1)全局注册
Vue.component(‘my-com’, component);
如果一个组件被多个其他组件调用,用全局注册较好
2)局部注册
如果一个组件在一个实例或者组件内部被多次调用,用局部注册较好
new Vue({
el: '#app',
data: {},
components: {
'my-com': component
}
})
4.组件交互
1)父组件向子组件传参
父组件使用prop属性,向子组件传参
动态传参
没有:传递的常量字符串
有: 传递boolean、number、对象、数组、变量
子组件接受参数
let component = { props: ['title','msg'], } props校验 let component = { props: { title: { type: String, // 验证数据类型 required: true, // 该参数必需传递的参数 // 基本数据类型直接写在default后面 default: '你好', // 当前参数的默认值 // 引用数据类型,使用工厂函数的形式返回一个对象或者数组 default() { return { } } validator(val) { // 自定义的验证器 return val.length > 3 } } }, }
2)子组件向父组件传参
```
let component = {
data() {
return {
comMsg: '我是子组件数据'
}
},
template:`
`,
methods: {
clickHandler() {
this.$emit('son-handler', this.comMsg)
}
}
}
new Vue({
el:'#app',
data: {
fatherDate: ''
},
methods: {
fatherHandler(val) {
// val就是子组件发送事件时,携带的参数this.comMsg
this.fatherDate = val
}
},
components: {
myCom: component
}
})
3)单向数据流
父级的prop数据的更新,会向下流动到子组件中,反过来不行
父组件能够修改子组件中的数据,而子组件不能直接修改父组件中的数据
作用:
防止子组件意外变更状态,会影响父组件中的状态或者数据
5.插槽
1)普通插槽
hello vue
let com = {
template: `
// 默认插槽
`
}
```
2)具名插槽
简写 #header
我是头部内容
hello component
你好组件插槽
我是脚步内容
let com = {
template: `
// 具名插槽
// 匿名插槽
`
}
3)作用域插槽
{{scope.msg}} // scope是作用域对象,内部封装了子组件传递上来的msg数据,可以使用对象的解构
我是头部内容
hello component
你好组件插槽
我是脚步内容
let com = {
data() {
return {
msg: '我是组件数据'
}
}
template: `
// 具名插槽
// 匿名插槽
`
}
6.动态组件
// 静态组件
// 动态组件
1.混入
混入规则:
1)数据模型中的数据,会进行递归合并,出现同名属性,以组件中的数据为准
2)同名的构造函数会合并成数组,其中的函数都会调用,混入的钩子执行在前,组件的钩子执行在后
3)内部对象的合并,类似于components、methods回合并为一个对象,发生冲突时,以组件中的为准
全局混入
Vue.mixin({})
局部混入
mixins: [mixin]
2.自定义指令
指令内部有5个钩子函数
bind(){}
inserted(){}
update(){}
componentUpdated(){}
unbind(){}
每个钩子函数内部都有4个形参
el 当前指令绑定的dom对象
binding 当值绑定指令的详细信息
vNode Vue编译生成的虚拟dom节点
oldNode 上一个虚拟dom节点,只有在update和componentUpdated这两个钩子触发时,该参数才有实参
3.过滤器
使用场景
{{timestamp | fmtDate}}
v-bind=“msg | fmtText”
全局注册
Vue.filter(‘fmtDate’, (val) => {
return …val
})
局部注册
filters: {
fmtDate(val) {
return …val
}
}
4.render函数
render(createElement) {
return createElement(nodeName,props,childVnode)
}
createElement函数内部参数说明
nodeName: 标签名
props: 该元素的配置信息
‘class’、style、attrs、props、domProps、On、nativeOn
childVnode: 子虚拟节点,一般为数组,表示其子元素有多个
5.plugin插件
plugin插件需要提供一个install方法
在vue实例构建之前去调用该插件
保证创建出来的vue实例能够访问插件中的全局方法或属性
Vue.user(MyPlugin)
let vm = new Vue({})
vue-router是vue的一个插件,用来提供路由功能。
通过路由的改变可以动态加载组件,达到开发单页面程序的目的。
1.基本使用
1)声明组件
let com1 = {template:`this is com1`}
let com2 = {template:`this is com2`}
let com3 = {template:`this is com3`}
2)声明路由器
router是路由器对象,routes是路由列表
let router = new VueRouter({
routes: [
{
path: '/a', // 路由地址
component: com1,// 对应组件
name: 'comA', // 给路由设置名字,方便使用name进行跳转
redirect: '/c', // 重定向
alias: '/coma', // 别名
},
{ path: '/b', component: com2 },
{ path: '/c', component: com3 },
]
})
3)路由注册
在vue的根实例中,注册路由
let vm = new Vue({
el: '#app',
router: router
})
4)路由使用
<router-link to="/a">A组件</router-link>
<router-link to="/b">B组件</router-link>
<router-link to="/c">C组件</router-link>
// 路由视口,用来加载路由对应的组件
<router-view></router-view>
2.动态路由
1)声明
let router = new VueRouter({
routes: [
// 动态路由参数以冒号开头
{ path: '/user/:username/:id', component:com1 }
]
})
<router-link to="/user/zhangsan/1001"></router-link>
2)获取路由携带参数(参数值会被设置到 this.$route.params中)
/user/zhangsan/1001和/user/lisi/1002都将映射同一个组件,
在一个组件内部发生动态路由变化时,vue组件实例会被复用,而不会重新销毁再创建
组件内部发生动态路由变化,想要监听路由参数变化时,可以使用watch监听$route,或者使用组件内部的导航守卫
let com1 = {
data() {
return {
id: null,
username: null
}
},
template: ``,
created() {
// 在created钩子函数中,只能一次性获取携带的参数,不能够持续获取
this.id = this.$route.params.id;
this.username = this.$route.params.username;
},
watch() {
// 使用watch监听器,可以持续监听路由器对象中,携带参数的变化
$route(to, from) {
this.id = to.params.id;
this.username = to.params.username;
}
}
}
3.路由守卫
1)介绍
正如其名,vue-router提供的导航守卫主要用来通过跳转或取消的方式守卫导航。
每个守卫方法接收三个参数:
to: Route 即将要进入的目标 路由对象
from: Route 当前导航正要离开的路由
next: Function 一定要调用该方法来 resolve 这个钩子,让路由跳转继续进行
next(): 进行管道中的下一个钩子(继续完成导航跳转)
next(false): 中断当前的导航。
next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址。
守卫是异步解析执行,导航在所有守卫 resolve 完之前一直处于等待中。
2)全局守卫
let router = new VueRouter({
routes: [
{...}
]
})
// 全局前置守卫
router.beforeEach((to, from, next) => {
.......拦截操作
next();
// next(false);
})
// 全局后置守卫
router.afterEach((to, from) => {
.......之后操作
})
3)路由独享守卫
let router = new VueRouter({
routes: [
{
path: '/a',
component: com1,
beforeEnter(to, from, next) {
.......拦截操作
next();
}
}
]
})
4)组件内部守卫
let com1 = {
data(){
return {}
},
template: ``,
// 进入当前组件之前拦截
beforeRouteEnter(to, from, next) {
.......拦截操作
next();
},
// 在当前组件中,路由发生改变时触发
beforeRouteUpdate(to, from, next) {
.......拦截操作
next();
},
// 离开当前组件之前,触发
beforeRouteLeave(to, from, next) {
.......拦截操作
next();
}
}
4.嵌套路由
品字形布局:上面头部,左侧导航栏,右侧内容区域
嵌套路由的定义(children属性中定义子路由)
let router = new VueRouter({
routes: [
{
path: '/student',
component: comStu,
children: [
{ path: 'grade', component: comGrade },
{ path: 'register', component: comRegister },
]
}
]
})
使用
一级路由对应一个router-view
二级路由对应一个router-view
5.编程式导航
1)this.$router.push() // 在history历史栈中新增一条记录
this.$router.push({ path: '/a' })
this.$router.push({ name: 'comA' })
路由跳转并携带参数
· 路径path跳转和query搭配使用
this.$router.push({ path: '/a', query: { username: 'zhangsan' } })
· 名字name跳转和params搭配使用
this.$router.push({ name: 'comA', params: { age: 12 } })
参数获取
在跳转的目标组件中,只要能够访问到$route就能拿到跳转携带的参数
例如:在目标组件的created(){
console.log(this.$route.params)
console.log(this.$route.query)
}
query传参和params传参区别
1.query传参参数保存在url地址栏中,以查询字符串的形式拼接上去
2.安全性来讲,params比较安全,query会显示在地址栏中,容易造成数据泄露
3.刷新页面时,query中的数据不受影响,而params中的数据直接消失
2)this.$router.replace() // 直接替换当前路由,不会产生新的历史记录
3)this.$router.go() // 内部接受正值或负值的整数作为参数,正数就是前进,负数就是后退(在历史记录中)
6.路由模式
1)修改路由模式
let router = new VueRouter({
// mode: 'hash',
mode: 'history', // mode属性修改路由模式,默认为hash模式
routes: []
})
hash路由和history路由的区别:
1.hash路由在地址栏URL上有#,而history路由没有#,并且更精简
2.进行回车刷新操作,hash路由会加载到地址栏对应的页面,而history路由一般就404报错了
(刷新是网络请求,history路由需要后端支持)。
3.hash路由支持低版本的浏览器,而history路由是HTML5新增的API。
4.hash的特点在于它虽然出现在了URL中,但是不包括在http请求中,所以对于后端是没有一点影响的,
所以改变hash不会重新加载页面,所以这也是单页面应用的必备。
5.history运用了浏览器的历史记录栈,之前有back,forward,go方法,之后在HTML5中新增了
pushState()和replaceState()方法(需要特定浏览器的支持),它们提供了对历史记录进行修改的功能,
不过在进行修改时,虽然改变了当前的URL,但是浏览器不会马上向后端发送请求。
#### 六、Vuex状态管理机
1.介绍
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。
它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
应用场景:
同级别组件之间通讯
let comA = {}
let comB = {}
2.基本使用:
1)state(类似于vue实例中data)
存储数据的地方,将每个vue实例中的公共变量抽取出来进行统一管理
state: {
msg: 'hello vuex'
}
在vue中,通过计算属性获取存储在vuex的状态,可以直接获取,也可以通过辅助函数获取
computed: {
count() {
return this.$store.state.count
}
// 或者
...Vuex.mapState(["count"])
}
2)getters(类似于vue实例中的computed)
用于将state中静态的数据经过计算再返回
getter: {
reMsg(state) {
return state.msg.toUpperCase()
}
}
在vue中,可以通过计算属性访问
computed: {
reMsg() {
return this.$store.getters.reMsg
}
// 或者
...Vuex.mapGetters(["reMsg"])
}
3)mutations(突变)
更改Vuex的store中的状态的唯一方法是提交 mutation,必须是同步操作。
mutation函数的第一个参数为state ,第二个参数是来接受在调用mutation的时候用户传递的实参
mutations: {
SET_MSG(state, payload) {
state.msg = payload
}
}
可以在vue生命周期钩子中提交突变或者在methods中映射突变方法
created() {
// 参数一是突变方法的名字,参数二是传递的实参
this.$store.commit('SET_MSG', 'hello mutation')
}
methods:{
...Vuex.mapMutations(['SET_MSG'])
}
4)actions(动作)
用来存放异步请求方法,action提交的是 mutation,从而间接的变更state中的状态
actions中的异步请求接受一个形参-context(上下文对象)
可使用context.state获取state
可使用context.getters获取getters
可使用context.commit()提交突变
可使用context.dispatch()分发动作
actions: {
async findAll(context) {
let res = await axios.get('http://...');
// 提交突变
context.commit('SET_MSG', res.data.data)
}
}
可以在vue生命周期钩子中分发动作或者在methods中映射actions中的方法
created() {
// 参数一是异步函数的名字,参数二可以传递实参
this.$store.dispatch('findAll')
}
methods:{
...Vuex.mapActions(['findAll'])
}
5)案例-定义状态机实例
let store = new Vuex.Store({
state: {
msg: 'hello vuex'
},
getters: {
reMsg(state) {
return state.msg.toUpperCase()
}
},
// 突变
mutations: {
SET_MSG(state, payload) {
state.msg = payload
}
},
// 动作
actions: {
async findAll(context) {
let res = await axios.get('http://');
// 提交突变
context.commit('SET_MSG', res.data.data)
}
}
})
6)通过store将状态机实例注入进去
let vm = new Vue({
el: '#app',
store
})
3.辅助函数
Vuex构造函数内部提供了快捷映射数据、方法的方法
使用对象的解构,将内部的辅助函数解构出来
let { mapState, mapGetters, mapMutations, mapActions } = Vuex
使用
computed: {
...mapState(['msg']), // 状态映射
...mapGetters(['reMsg']) // 计算属性映射
},
methods: {
...mapMutations(['SET_MSG']), // 突变映射
...mapActions(['findAll']) // 动作映射
}
4.模块化开发
1)目的
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。
当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。
每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块。
namespaced表示设置命名空间
2)使用
let moduleA = {
namespaced: true,
state() {
return {}
},
getters: {},
mutations: {},
actions: {}
}
let moduleB = {
namespaced: true,
state() {
return {}
},
getters: {},
mutations: {},
actions: {}
}
let store = new Vuex.Store({
modules: {
// 给状态机模块重命名
a: moduleA,
b: moduleB
}
})
let vm = new Vue({
el: '#app',
data: {},
computed: {
// 映射状态,第一个参数是模块的名字
...mapState('a',['aData']),
...mapState('b',['bData']),
// 映射计算属性,第一个参数是模块的名字
...mapGetters('a',['reAData']),
...mapGetters('b',['reBData']),
},
methods: {
// 映射突变
...mapMutations('a', ['SET_ADATA']),
...mapMutations('b', ['SET_BDATA']),
// 映射动作
...mapActions('a', ['findAll])
...mapActions('b', ['query])
},
created(){
this.findAll() // 调用/执行方法
},
store: store
})
网站 https://webpack.docschina.org
文档 https://webpack.docschina.org/concepts/
简介:本质上,webpack 是一个前端资源构建工具,用于现代 JavaScript 应用程序的静态模块打包工具。
当webpack 处理应用程序时,它会在内部构建一个 依赖图(dependency graph),
此依赖图对应映射到项目所需的每个模块,并生成一个或多个 bundle。
全局下载包
cnpm install -g webpack webpack-cli
或者cnpm i -g webpack webpack-cli
局部下载包
初始化项目npm init -y
下载包
cnpm i --save-dev webpack webpack-cli
或者cnpm i -D webpack webpack-cli
五大模块
入口(entry)
输出(output)
loader加载器 (module)
插件(plugin)
模式(mode)
1.入口
入口起点(entry point)指示 webpack 应该使用哪个模块,来作为构建其内部 依赖图(dependency graph) 的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。
默认值是 ./src/index.js,但你可以通过在 webpack configuration 中配置 entry 属性,来指定一个(或多个)不同的入口起点。例如:
webpack.config.js
module.exports = {
//单入口
entry: ‘./path/to/my/entry/file.js’,
//多入口
entry: {
app: ‘./src/app.js’,
adminApp: ‘./src/adminApp.js’
}
};
2.输出(output)
output 属性告诉 webpack 在哪里输出它所创建的 bundle,以及如何命名这些文件。主要输出文件的默认值是 ./dist/main.js,其他生成文件默认放置在 ./dist 文件夹中。
你可以通过在配置中指定一个 output 字段,来配置这些处理过程:
webpack.config.js
const path = require(‘path’);
module.exports = {
entry: ‘./path/to/my/entry/file.js’,
output: {
//filename: ‘my-first-webpack.bundle.js’,
filename: ‘./js/my-first-webpack.bundle.js’,
path: path.resolve(__dirname, ‘dist’),
}
};
path 模块是什么,它是一个 Node.js 核心模块,用于操作文件路径。resolve()用来拼接路径
3.loader
webpack 只能理解 JavaScript 和 JSON 文件,这是 webpack 开箱可用的自带能力。loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效 模块,以供应用程序使用,以及被添加到依赖图中。
在更高层面,在 webpack 的配置中,loader 有两个属性:
test 属性,识别出哪些文件会被转换。
use 属性,定义出在进行转换时,应该使用哪个 loader。
webpack.config.js
const path = require('path');
module.exports = {
output: {
filename: 'my-first-webpack.bundle.js'
},
module: {
rules: [
{
test: /\.html$/,
use: 'html-loader'
}
]
}
};
4.插件(plugin)
loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量。
想要使用一个插件,你只需要 require() 它,然后把它添加到 plugins 数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用 new 操作符来创建一个插件实例。
webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 通过 npm 安装
const webpack = require('webpack'); // 用于访问内置插件
module.exports = {
module: {
rules: [
{ test: /\.txt$/, use: 'raw-loader' }
]
},
plugins: [
new HtmlWebpackPlugin({template: './src/index.html'})
]
};
在上面的示例中,html-webpack-plugin 为应用程序生成 HTML 一个文件,并自动注入所有生成的 bundle。
5.模式(mode)
通过选择 development, production 或 none 之中的一个,来设置 mode 参数,你可以启用 webpack 内置在相应环境下的优化。其默认值为 production。
6.=entry入口=======
在webpack.config.js文件中
module.exports = {
//单入口
//entry:'./src/js/index.js',
//多入口
entry: {
index: './src/js/index.js',
index2: './src/js/index2.js'
},
}
7.=output输出=======
在webpack.config.js文件中
const {resolve} = require('path');
module.exports = {
output: {
//单出口
//filename: './js/built-dev.js',
//多出口
filename: './js/[name]-built-dev.js',
// 输出路径写绝对路径
// __dirname nodejs的变量,代表当前文件的目录的绝对路径
path: resolve(__dirname, 'build')
}
}
8.=loader module=======
在webpack.config.js文件中
module.exports = {
module:[
//loader的配置
....
]
}
9.转义js文件,排除node_modules中的文件
babel-loader
安装 cnpm i -D @babel/core @babel/preset-env babel-loader
配置
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}