安装
npm install -g @vue/cli && vue create my-project
可以在
new Vue
之前使用window.函数名=function() {}
创建一个全局的函数方法在
vue
中使用bootstrap
可以直接用BootstrapVue
,如果要单独安装可以这样做:npm install --save bootstrap jquery popper.js
,然后在main.js
中加入即可1
2import 'bootstrap/dist/css/bootstrap.min.css'
import 'bootstrap/dist/js/bootstrap.min'
配置
vue.config.js
- 用vue-clic3脚手架新建的项目,默认是没有vue.config.js文件的,可以手动创建,如果项目根目录里面有它,它会被@vue/clic-service
1 | const CopyWebpackPlugin = require('copy-webpack-plugin'); |
自动加载模板语法
- 模板中如果遇到这种类型的三目运算符
a ? a : b
,最好用{a || b}
来代替
1 | // v-bind |
动态数据绑定
1 | // 动态绑定class,这是可以和原有class共存的 |
slot插槽
感觉就是一个简化了的模板,模板有特定的语法传入变量,而这里只是留了一个位置以供插入任何内容
说高大上点就是实现了一套内容分发的API,将
<slot></slot>
元素作为承载分发内容的出口。如果没有插槽,那么在组件标签内的内容是不起作用的,例如
1
2
3
4
5
6
7
8
9
10
11
12
13
14// 我们定义这样一个组件
Vue.component('mycomponent', {
template:`<div>原始内容</div>`
})
// 然后打算在外部这样使用它
<mycomponent>其它内容</mycomponent>
// 如果没有插槽,则会渲染为<div>原始内容</div>,使用者的内容没有起到任何作用,这时候如果使用插槽,可以这样定义
Vue.component('mycomponent', {
template:`<div>原始内容<slot></slot></div>`
})
// 同样的时候方式就会输出<div>原始内容其它内容</div>了
具名插槽
给插槽指定一个名字,常用于一个组件包含多个插槽的情况,例如:
1 | // 这样定义组件 |
作用域插槽
指组件上定义的属性,可以在组件元素内使用。例如
1 | Vue.component('mycomponent', { |
路由
vue router
默认在跳转新页面会保持当前页面的scroll
,有时候我们需要在新页面手动window.scrollTo(0, 0)
路由定义
1 | { |
路由跳转
- 需要注意的是,如果跳转到相同的路由,页面不会重新渲染(mounted等这些不会重新执行,即使query/params参数不一样也不会),如果要实现相同路由能够刷新,可以在template下最外层的元素上面加入一个
:key="routeParams"
1 | // 方法一 |
页面Script相关方法
页面生命周期
- beforeCreate: 这个时候还不能调用vuex的方法
- created
- beforeMount
- mounted: 实例被挂载后调用,用得最多
- beforeUpdate
- updated
- activated
- deactivated
- beforeDestroy
- destroyed
- errorCaptured
实例/组件属性
$forceUpdate
: 强制页面重新渲染$options
: 获取用户自定义的配置1
{{abc | $options.filters.myFilters}}
parent
: 从子组件访问父组件的实例$ref
: 可以通过this.$refs.xxx
来访问当前组件的指定的子组件/元素常用于获取表单1
2
3
4
5
6
7
8
9<el-form :model="formData" :rules="rules" ref="testForm" @validate="validateForm" inline-message></el-form>
<my-component ref="mine"></my-component>
<script>
const fields = this.$refs.contactForm.fields
this.$refs.contactForm.validate(async (valid) => {})
this.$refs.mine.myMethod()
this.$refs.mine.offsetHeight // 获取元素高度宽度
</script>$root
: 用来访问vue
的根实例$watch
: 跟watch
用法一样1
this.$watch('msg', function (oldValue, newValue) {})
全局API
Vue.nextTick
- 在下次DOM更新循环结束知乎执行延迟回调,在修改数据之后立即使用这个方法,可以在该方法里面获取到更新后的DOM
- 因为本质上数据修改后DOM的更新是异步的,该方法提供了一个等待DOM渲染完成后的回调操作
1 | // 修改数据 |
computed
- 用于计算一些
props
或data
无法直接得到的变量 - 不会立马取值,用到的时候才会取值,并且有缓存,依赖数据不改变不会更新结果
- 在非严格模式下,如果用引用的方式对数据进行修改,例如
myAttr[key]
,可能会发生值改变了但是set
却没有触发器的情况
1 | <script> |
针对类似的get/set方法批量创建computed属性,可以参考类似
mapGetters
的写法原理1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18const myProps = (computedNames: string[]) => {
const computedProp: any = {};
for (const prop of computedNames {
computedProp[prop] = {
get() {
return 'computed' + prop;
},
set(newValue: any) {
......
}
}
}
return computedProp;
}
computed: {
...myProps(['value1', 'value2', 'value3']),
}
directives指令
- 指令,如果直接写在组件的
script
中则是局部的,这个用得少 - 定义了后就可以在要使用的标签上添加
v-xxx
,其中的xxx
为指令名字 - 指令实例: Vue实现简单的鼠标拖拽滚动效果
document对象
- 同样可以获取document对象
1 | document.body.clientWidth // 获取屏幕宽度 |
props
1 | props: { |
filter
- 过滤器,如果直接写在组件的
script
中则是局部的 - 在filter中无法使用上下文
this
,因为它设计来就仅仅是为了filter
1 | <template> |
watch
- 用于观测值的变化,执行相应的函数
- 子组件如果有个单独的初始化函数可以用它来监听某个
prop
的值变化,变化了则可以执行一次初始化函数 - 与
computed
不同的是,它会立即执行,会先算出来一个老值,数据变化就执行函数
1 | <script> |
组件通信
父子组件通信
broadcast + dispatch
的方式已经弃用了子组件如果要监听父组件值的变化,可以直接用
watch
监听props
,当然如果子组件需要单独修改这值,可以在datas
里面另外定一个一个变量,例如:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20props: {
field1: Boolean
},
data (props) {
return {
field1Rename: props.field1
}
},
watch: {
field1: {
handler: function () {
field1Rename = this.field1 // 每次父组件改变该prop的时候子组件需要恢复到初始值
}
}
},
methods: {
changeField () {
this.field1Rename = false // 子组件能够单独修改该字段
}
}下面示例列出了两种方式
1 | // 父组件 |
非父子组件的通信
- 用
$bus
或者$root
应该都能实现 - 其实是一种发布/订阅模式,谁都能订阅
1 | // 组件一 |
页面样式
经常给
style
标签添加scoped
属性,表示当前CSS只作用于当前组件中的元素(其实现就是给元素添加data-xxx属性),当然可以在一个组件中写两个不同的style
以混用全局和局部样式。使用
scoped
后,父组件的样式不会渗透到子组件,但是一个子组件的根节点会同事受其父组件有作用域的CSS和子组件有作用域的CSS的影响。所以父组件的样式调整可能会影响到子组件如果不使用
scoped
,最好在需要覆盖的样式前加上顶级作用域:1
2
3.myDiv .el-tag {
color: black;
}
事件处理
事件监听
1 | // 可以直接在监听里写语句 |
事件修饰符
- 用得最多的是拿来阻止某些事件的发生或冒泡,例如
@keydown.enter.native.prevent
可以阻止textarea
输入换行符,@keyup.enter.native.stop
阻止回车时间向上冒泡
1 | <!-- stop: 阻止单击事件继续传播 --> |
手动触发事件
- 由于可以通过
this.$refs.refName.$el
的方式得到原生的元素,所以原生的方法都能使用
1 | this.$refs.myEle.dispatchEvent(new MouseEvent('mouseenter')) // 手动触发原生mouseenter事件 |
常用事件
- 如果是普通的
div
标签可以直接加@click
监听,但是对于自定义的组件则应该加上native
修饰符才行,监听组件根元素的原生事件
1 | // 鼠标按下 |
异常处理
捕获全局异常可以这样做:
1 | // 除了errorHandler,还有 |
Vuex
- 专为
Vue.js
应用程序开发的状态管理模式,采用集中式存储管理应用的所有组件的状态v - 优点:
- 解决了多个视图依赖于同一状态,来自不同视图的行为需要变更同一个状态的情况,有了
vuex
则就集中式管理了,否则可能需要再每次切换页面将所有的状态都带上才行 - 也能实现父子组件的数据共享
- 与全局变量不同,它的状态存储是响应式的。当Vue组件从store中读取状态的时候,若store中的状态发生变化,那么相应的组件也会相应地更新
- vuex是存在于内存中的,刷新页面会丢失数据。强烈建议使用
vuex-persistedstate
插件将state
持久化至localstorage/cookie中,以防刷新页面数据丢失,只需要定义时加入一个plugin即可new Vuex.Store({plugins: [createPersistedState()]})
- 从列表到详情页面无需等待接口去重新请求可以直接先拿列表中已经存在的数据渲染,在路由跳转前获取详情数据,但无需等待
- 解决了多个视图依赖于同一状态,来自不同视图的行为需要变更同一个状态的情况,有了
- 不能直接改变
store
中的状态,改变的唯一途径就是显式地提交(commit) mutation - 需要遵守的响应规则:
- 最好提前在store中初始化好所有所需属性
- 当需要在对象上添加新属性时,应该选择下面的方法之一
- 使用
Vue.set(obj, 'newProp', 12)
- 以新对象替换老对象,例如
state.obj = { ...state.obj, newProp: 123}
- 使用
- 常见的应该放在vuex中的数据
- 需要在多个组件中访问的数据
- 系统配置项,比如应用的外观设置
- 用户基本信息,可能在不同的组件如header、content或者bottom都可能同时展示
- 几种不同的类型
- Getter
- 如果要在页面中派生出一个状态通常会用到
computed
,但是如果每个页面都需要同一个状态,就可以在store
中定义getter
(可以认为是针对store的计算属性computed) - 和
computed
一样,getter
的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算
- 如果要在页面中派生出一个状态通常会用到
- Mutation
- 提交mutation是改变store状态的唯一方法
- 每个
mutation
都有一个字符串的事件类型(type)和一个回调函数(handler)。这个回调函数就是实际进行状态更改的地方,并且它会接受state作为第一个参数 Mutation
必须是同步函数- 最好使用常量来替代``Mutation
事件类型,可以使
linter`之类的工具发挥作用
- Actions
- 类似于
mutation
,不过它提交的是``mutation`,而不是直接变更状态,并且可以包含任意异步操作 - 如果有异步的操作就交给
Action
,在它内部commit(mutation)
- 类似于
- Getter
一个完整的例子:
1 | const store = new Vuex.Store({ |
Module
- 为了防止单一的状态树对象过大,需要将
store
分割成模块module
,每个模块拥有自己的state/mutation/actin/getter
- 当然模块内部的mutattion和getter接收的第一个参数是模块的局部状态对象。对于action,局部状态也是
context.state
,根结点状态则为context.rootState
。作为模块内部的getter,根结点状态会作为第三个参数暴露出来,例如这样定义一个getter
:sumWithRootCount(state, getters, rootState)
可以这样定义module
:
1 | const moduleA = { |
静态文件
- 可以通过
@/assets/fileName
来访问assets
下的静态文件,例如<img src="@/assets/filaneme">
- img的src是无法动态解析的,一般都只能硬编码,不能放一个变量在这里,除非它是
base64
或者把图片放到src同级的static目录下然后用static/a.png这种方式来访问,因为url-lodaer
无法解析js动态生成的路径
存储
localStorage
(其实是H5的特性),主要用来作为本地存储使用,能够解决cookie存储空间不足的问题(每条cookie最大为4k),localStorage
默认5M大小。
- 如果要和cookie对比,一般数据量大的会选择使用
localStorage
,因为cookie每次和服务器交互都会带上,只适合小量的数据,例如用户token等信息。 - 仅在客户端保存,不参与和服务器的通信
- 无法再隐私模式下使用
- 生命周期是永久的,只要用户或者程序不主动清除,消息就永远存在,即使重启浏览器都在
sessionStorage
,刷新页面数据依然存在,但是关闭页面数据就不存在了
1 | localStorage.setItem('accessToken', 'Bearer xxxxxxxx') |
网络交互组件axios
- axios默认不会对url进行编码,可以使用
encodeURI
或者encodeURIComponent
对URL进行编码,前者会避开&/?/[/]
等url中的功能性字符
(Vue官方已经不推荐vue-resource,而是推荐axios了)用法其实与Ajax类似,例如:
1 | axios({ |
过渡动画
- 需要利用
transition
,并且需要写css
,参考这里,这里有4个Vue路由过渡动效
文件上传处理
1 | // template只需要input即可 |
TroubleShooting
- 更改数据页面不渲染,可能有如下原因
- 在给data赋值后只是简单地添加新的属性,没有用this.$set等方法,导致没有新添加的属性没有实现双向绑定,从而导致重新渲染失败。常见现象也有改变一个值,第一次改变页面渲染成功,之后再改变页面不会更新等
- 页面跳转出错/NavigationDuplicated: 页面跳转经常出现莫名其妙的错误,所以一般都会把异常直接忽略,例如
router.push('/next').catch(err => {})
- Maximum call stack size exceeded: 可能是引用组件的名字和当前组件的名字重复了,导致无限去import
- Error: Cannot resolve module ‘sass-loader’: 要使用sass/scss,需要先安装依赖
npm install -D sass-loader node-sass
- TypeError: this.getOptions is not a function: 原因可能是新版本
sass-loader@11.0.0
和vue@2.6.12
不兼容导致,可以尝试降级sass-loader
,设置为"sass-loader": "^10"
- 路由切换白屏: 可能有如下原因:
- template中在空变量上获取其属性值导致报错,例如
{ user.name }
,如果user
的初始值为null,那么如果刚进入页面user
在赋值前会报错,导致出现短暂的白屏
- template中在空变量上获取其属性值导致报错,例如
- You are using the runtime-only build of Vue where the template compiler is not available: 需要在
vue.config.js
中加上配置:runtimeCompiler: true
相关链接
- Vuex文档
- 仿造猫眼电影客户端实例: https://github.com/zhixuanziben/gouyan-movie-vue
- Objc中国的全平台客户端: https://github.com/halfrost/vue-objccn
- 仿闲鱼:https://github.com/Sukura7/vue-ali-xianyu
- 仿hackernews: https://github.com/vuejs/vue-hackernews-2.0
- Flask与Vuejs创建一个简单的单页应用https://testdriven.io/developing-a-single-page-app-with-flask-and-vuejs
- Vue表单可视化生成器
- 滚动加载插件vue-infinite-loading
- Bootstrap Vue
- Echarts Vue
- Vue 开发谷歌浏览器的脚手架
- Toasts Notification Bar 库: vue-toastification
- Table库:vue-good-table
- 时间选择库: web端用vue2-datepicker,移动端用vue-datetime,在使用datetime相关的组件的时候如果日期和时间在页面上是分开让用户选择的,他们一定要model到同一个变量上,否则可能会选择到之前的时间,另外,vue-datetime需要设置
value-zone=local
,这样用户前端才能看到自己本地的时间。顺便说一个逻辑问题,如果一个事件需要给不同时区的人看到,后端最好直接存储一个不包括时区的时间,在展示的时候只展示目的地的时间而不是当前用户的时区,这是比较合理的,比如国外有个电影就直接写国外什么时间而不是转换为国内的时间,否则不仅很累,用户还很疑惑 - qrcode.vue二维码生成库
- vue-rate: 评论打分组件
- vuelidate: 如果不实用element的验证功能,其他的vue项目就只能用这个去验证表单了,但终归没有ele好看
- 可以在error的v-if上添加一个
submitting
变量控制提交表单的时候才显示错误
- 可以在error的v-if上添加一个
- 对于简单的项目可以直接使用bootstrap-vue