前言
当前总结是本人在业余学习与实践过程后的总结与归纳,旨在检验自己的积累,也方便忘记时查阅,同时也希望能帮助那些这方面知识匮乏的同行们,总结是基于vue2.x
,vue-cli3.x
,主要记录些,vue
常用的指令、事件,监听、数据绑定、过滤器、组件、动画、vuex
,vue-router
等日常工作中时常用到的东西,也有些常用的插件
和开发工具
的介绍与使用,以及部分性能优化
的建议与实践,如有不对,或不足的地方,也请各位大神,指出来,学习学习。
基础
1. 理解mvvm
m
是vue
实例中的data
,自定义的数据或后端返回的数组 不是后端mvc
里的model
概念不同。vm
是vue
的实例m
和v
之间的调度者 是mvvm
的核心思想v
是html
要渲染的。
2. 常用指令
v-cloak
解决{{}}插值闪烁问题v-text
会先执行 覆盖 元素中原本的内容 但是插值表达式只会覆盖自己的占位符,默认不会闪烁v-html
渲染 html标签 覆盖元素中原有元素内容v-bind
: 简写为: 用来绑定数据 可以写合法的js表达式v-on
: 简写为@
用来点击事件
3.常用事件修饰符
-
stop
阻止冒泡 :外层和里层都有方法 点击里层会产生冒泡,也会触发外层的事件。 顺序 从里到外产生事件 -
prevent
阻止浏览器默认行为 :a
标签有浏览器默认行为。 -
capture
捕获事件 :点击里层先触发外层再触发里层 顺序从外到里产生事件 -
self
只触发自己本身的事件 不会产生冒泡和捕获事件 类似于阻止冒泡 但只针对自己那一层 最外层还是会被最里层冒泡冒到stop
是阻止所有层次 -
once
事件只执行一次
4.数据的绑定
v-bind
: 数据的单向绑定v-modle
:数据的双向绑定 这个只能用于表单元素中
tips: 表单元素
radio
text
address
select
checkbox
textarea
- 组件嵌套:子组件里有v-model,父组件也有v-model的实现多层的双向绑定
//子组件
<template>
<div class="yx-from-item">
<el-form-item :label="label"
:prop="prop">
<el-select v-model="newModelValue"
filterable
clearable
:placeholder="placeholder">
<el-option v-for="item in options"
:key="item.label"
:label="item.label"
:value="item.value"></el-option>
</el-select>
</el-form-item>
</div>
</template>
export default {
//双向数据绑定--核心
model: {
prop: 'modelValue',//要存在于props
event: 'change'//当组件的值发生改变时要emit的事件名
},
props: {
label: {
type: String,
default: () => {
return ''
}
},
prop: {
type: String,
default: () => {
return ''
}
},
placeholder: {
type: String,
default: () => {
return '选择学校'
}
},
//下拉
options: {
type: Array,
default: () => {
return [
{
label: '开启',
value: 1
},
{
label: '关闭',
value: 0
},
];
}
},
//数据绑定的值--核心
modelValue: {
default: () => {
return '';
}
}
},
data () {
return {
newModelValue:this.modelValue//核心
};
},
mounted () {
},
watch: {//这里检测子组件中的data中的值,一旦发生 变化就提交事件到父组件--核心
newModelValue: function (newVal,oldval) {
this.$emit('change', newVal)
},
//父组件修改了model值也要赋值给子组件--核心
modelValue: function (newVal, oldval) {
this.newModelValue = newVal
}
},
}
//父组件使用
<yx-form-item
label="所属学校:"
prop="school_id"
v-model='leftForm.model.school_id'
:options='leftForm.schoolOptions'>
</yx-form-item>
data () {
return {
/*表单*/
leftForm: {
//学校选项
schoolOptions: [
{
label: "工程",
value: 2
},
{
label: "华南",
value: 1,
className: 'ml-32'
}
],
//绑定的值 rules 规则也使用到的值
model: {
school_id: '', //学校id
grade_id: '',//年级
branch_id: '',//学院
specialty_id: '',//专业
class_id: '', //班级
st_name: '',//学生姓名
realname: '',//真实姓名
alias: '',//学生昵称
user_name: '',//登录账号、学号
birthday: '',//出生日期
password: '',//登录密码
weixin_userid: '',// 微信账号
mobile_phone: '',//手机号
system: '',//学制
state: '',//学籍状态 --账户状态
graduate_year: '', //毕业年份
teachername: '',//辅导员
sex: '',//性别
content: '',//介绍
role: [],//角色
dorm_id: '',//宿舍号id
building_id: '', //楼号id
},
//规则
rules: {
state: [{ required: true, message: "请选择", trigger: "change" }],
school_id: [
{ required: true, message: "请选择", trigger: "change" }
],
branch_id: [{ required: true, message: "请选择", trigger: "blur" }],
user_name: [{ required: true, message: "请输入账号", trigger: "blur" }],
password: [{ required: true, message: "请输入密码", trigger: "blur" }],
mobile_phone: [
{ validator: this.$Element.isNum, trigger: 'blur' },
{ min: 11, max: 11, message: '请输入11位的手机号', trigger: 'blur' },
],
// sex: [{ required: true, message: "请选择性别", trigger: "change" }]
},
}
}
5.class
绑定
1.数组带对象
<div :class="[classA,classB,{'classC':flag}]" >
data(){
return{
flag:true
}
}
tips:可以在类中的数组中写三元表达式,但推荐使用对象来代替它控制是否渲染
2.单纯的对象
<div :class="{classA:falg1,classB:flag2}" />
data(){
return{
falg1:true,
falg2:true
}
}
3.数组带三元
<div :class="['ac','bd',falg?'active':'']" / >
data(){
return{
falg:true,
}
}
4.对象升级
<div :class="classObj" />
data(){
return{
classObj:{classA:falg1,classB:flag2}
}
}
tips:直接使用一个对象数组来控制样式
5.使用style
的对象来实现样式的修改
<div :class="styleObj" />
data(){
return{
styleObj:{color:red}
}
}
5.使用style
的数组带对象来实现样式的修改
<div :class="[styleObj1,styleObj2]" />
data(){
return{
styleObj1:{color:red},
styleObj2:{color:red}
}
}
6.v-for
的使用
1. 可以遍历: 普通数组,对象数组,对象,还可以是数字
<div v-for='(item,key,index) in object' :key='index'>
{{item}}--{{key}}--{{index}}
</div>
<div v-for='(count in 10)'> </div>
tips:在遍历对象的时候有多个
index
索引,遍历数字时是从1开始的。绑定key
时属性值必须是number
或者string
7.v-if
、v-show
v-if
有较高的切换性能 , 适合元素可能永远不会被用户看到。v-show
有较高的初始渲染消耗,适合元素频繁切换。
8 调试插件
-
在谷歌商店找
vue-devtools
插件,使用这个插件 并设置插件,允许访问文件网址。 会在调试中出现vue相关的东西 -
debugger
直接写可以调试
9 过滤器
全局和私有过滤器
<div v-for='(item,key) in object' :key='index'>
{{item | dateFormat}}
</div>
<div v-for='(count in 10)'> </div>
- 全局
import Vue form 'vue'
Vue.filter('过滤器名称',function(){
do some
})
- 私有(局部)
filters:{
dateFormat:function(data,param){
do some
}
}
tips:
data
就是|
第一个参数已经被定死了,永远是, 管道左边要被转换的数据,param 是过滤方法传进来的其他参数,过滤器采用就近优先原则,如果私有和全局的名称一样就优先采用私有的。padstart
和padend
es6
的补0
方法- 第二个参数是字符串,第三个参数是表达式,如果自己定义参数值是动态的会报错,还未找到原因,后期会找时间再看看,目前就是
简单的过滤用过滤器
,复杂点用方法
,能用计算属性用计算属性
,有缓存,能提高性能
10 按键修饰符
- 监听pc键盘上的值
<input @keyup.enter='方法名'></input>
tips:
enter
可以换成键盘上的任何一个值,只要去找相关的键盘码,就都可以使用,推荐设置个别名,放在没有按钮操作的模板。
- 自定义全局按键修饰符
Vue.config.keyCodes.f2=113
,就可使用了
tips:
f2
修饰符是vue
里没有定义的自己创建。
11 定义指令
1. 全局
定义的指令都要按规定去创建 在bind
和
inserted
还有 updated
中去创建
Vue.directive('focus'{
//每当指令绑定到元素上的时候,会立即执行bind 函数,只执行一次,
注意:在元素刚绑定元素的时候,还没有插入到dom中去,这时候,调用focus方法没有作用,即放在focus 放在bind中是不起作用 的
bind:function(el,binding){
el.style.color=binding.value
},
//表示元素插入到dom中的时候,只执行一次
inserted:function(){
el.focus() js行为放在这里去创建
},
//当组件更新的时候,可能会触发多次
updated:function(){},
})
tips:
参数1
指令名称,在定义的时候,指令名称前面不需要加v-前缀,但是调用的时候,必须在指令名称前加v-前缀;参数2
:是一个对象,在这个对象身上,有一些指令相关的函数,这些函数可以在特定的阶段,执行 相关的操作。- 在每个函数中,的第一个参数,永远是
el
,el
是代表被bind
绑定的元素,el
是个原生的js
对象。 第二个参数可以是用户传进来值bingding.value
2. 局部
directives:{
'指令名':{
bind:function( el,b){
}
}
}
3. 简写
'指令名':function(el,binding){
} //注意这个function 等同于 把代码写到bind和update中去
tips: 样式相关的指令放在
bind
中,js
行为相关的放在inserted
中比较合适,防止指令不生效。使用场景 写组件时可以用这个去改样式
12 生命周期
-
beforeCreate()
:这是我们遇到的第一个生命周期函数,表示实例完全被创建出来之前,会执行它... -
created()
: 这是遇到的第二个生命周期函数... -
beforeMount()
:这是遇到的第3个生命周期函数,表示 模板已经在内存中编辑完成,但是尚未把模板渲染(挂载)到页面中。在beforeMount
执行的时候,页面中的元素,还没有被真正替换过来,只是之前写的一些模板字符串。就像{{text}}这样 -
mounted()
:这是遇到的第四个生命周期函数,表示内存中的模板,已经真实的挂载到了页面中,用户已经可以看到渲染好的页面了。只要执行完这个生命周期,就表示整个vue
实例已经初始化完毕了,此时,组件已经脱离了创建阶段,进入到了运行阶段。 -
beforeUpdate()
:这时候表示,我们的界面还没有被更新[但数据已经被更新了页面中显示的数据,还是旧的,此时data
数据是最新的。页面尚未和最新的数据保持同步 -
update()
: 这一步执行的是 先根据data
中最新的数据,在内存中重新渲染出一份最新的内存dom
树,当最新的内存dom
树被更新后,会把最新的内存DOM
树重新渲染到真实的页面中去,这时候,就完成了数据data(model层)->view(视图层)
的更新, 页面和data
数据已经保持同步了,都是最新的。 -
beforeDestory
:当执行beforeDestory
钩子函数的时候,Vue
实例就已经从运行阶段,进入到销毁阶段, 当执行beforeDestroy
的时候,实例身上所有的data
和所有的methods
以及过滤器、指令...
都处于可用状态,此时,还没有真正执行销毁的过程。 -
destroyed
: 当执行这个函数的时候,组件已经被完全销毁了,此时,组件中所有的数据,方法,指令,过滤器...
都已经不可用了
13 过渡类名实现动画
1. vue的内置动画
<style>
.v-enter,
.v-leave-to{
opacity:0;
transform:translateX(150px) --这东西是位移
}
.v-enter-active,
.v-leave-active{
transition:all 0.4s ease;
}
</style>
<transition name='my'>
<h3 v-if="flag"></h3>
</transition>
<script>
data(){
return {
flag:false
}
}
</script>
2. 使用第三方类实现动画
<transition enter-active-class="bounceIn"
leave-avtive-class="bounceOut" duration='200'
>
<h3 v-if="flag" class="animated" ></h3>
</transition>
3. 在属性中声明js钩子 实现半场动画(只需要进场,不需要离场)
<transition
<div
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter"
>
</div>
</transition>
<transition
<div
v-show="flag"
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter"
>
</div>
</transition>
<script>
methods:{
beforeEnter(el){
//动画入场之前,此时动画尚未开始, 可以在beforeEnter中设置元素开始动画之前的初始位置
el.style.transform=
"translate(0,0)"
},
enter(el,done){
/*这句话,没有实际的作用,但是,如果不写,出不来动画效果,可以认为 这个会强制刷新动画,ofset触发了重绘重排导致动画更新了*/
el.offsetWidth
el.style.transform=
"translate(150px,450px)"
el.style.transition='all 1s ease'
/*这里的done 代表着
afterEnter的引用,
这个会立即执行afterEnter 否则会有延时
*/
done()
},
afterEnter(el){
/*动画完成之后,会调用afterEnter */
this.flag=!this.flag
}
}
</script>
4. 在实现列表过渡的时候,如果需要过渡的元素,是通过v-for
循环渲染出来的,不能使用transition
包裹,需要使用transitionGroup
<transition-group appear tag='ul'>
<li v-for >
<li>
</transition-group>
.v-enter,
.v-leave-to{
opacity: 0;
transform:translateY(80x);
}
.v-enter-active,
.v-leave-active {
transition: all 0.6s ease;
}
/*离开后下一个东西没有动画使用这个可以使用动画实现下一个东西渐渐地飘上来的效果,要和 v-leave-active的absolute 配合 固定写法*/
.v-move {
transition:all 0.6s ease
}
.v-leave-active{
/* absolute 有个特点元素默认宽度就是最小值,要在元素上添加width:100%*/
position:absolute;
}
<transition mode="out-in">
<component :is="comName" >
</component>
</transition>
tips:
v-enter
[这是一个时间点] 是进入之前,元素的起始状态,此时还没有开始进入v-leave-to
[这是一个时间点] 是动画离开之后,离开的终止状态,此时,元素动画已经结束了v-enter-active
[入场动画的时间段]v-leave-active
[离场动画的时间段]animated
是个动画库 新版本似乎不需要加入- 使用:
duration=200
来表示动画的时间 如果只写一个表示统一配置了开场和离场时间 用对象
可传入c入场和离场duration="{enter:200,leave:400}"
- 添加
appear
属性,实现页面刚展示出来,入场时候的效果- 通过为
transition-group
元素,设置tag
属性 指定transition-group
渲染为指定元素,如果不指定tag
属性,默认,渲染为span
标签mode="out-in" 先过渡再进来,防止有 阴影,通过
mode`来设置过渡方式。
注意:最外层一定要用
transition
包裹着,动画似乎升级了,可以在transition
标签中加入name
属性,并且在css
样式中把v
,替换为你的name
属性值。
14 组件
1.使用Vue.extend
来创建全局的Vue
组件
var coml=Vue.extend({
template:'<h3>这是使用Vue.extend 创建的组件</h3>'
})
//第一个参数组件名称,第二个参数创建出来的组件模板对象
Vue.component('myComl',coml)
<my-coml><my-coml/>
2.使用 vue.component
来创建组件
Vue.component('mycom2',{
template:'<div>
<h3>
这是直接使用Vue.component 创建出来的组件
</h3>
</div>'
})
3.使用 template
来创建组件
<template id='tmp1'>
<div>
<h1>
这里通过template元素,在外部定义的组件结构,这个方式,有代码的智能提示和高量
</h1>
</div>
</template>
Vue.component('mycom3',{
template:'#tem1'
})
- 私有组件
componment
<template id='temp2'>
<h1>这是私有login组件</h1>
</template>
componment:{
login:
{
template:'tmpl2'
}
}
-
如果使用了
Vue.component
定义了全局组件的时候,组件名称使用了驼峰命名,在引用的时候大写的驼峰要改为小写,同时两个单词之间 使用-链接 -
Vue.component
第一个参数:组件的名称,将来在引用组件的时候,就是一个标签形式来引入的,第二个参数:Vue.extend
创建的组件,其中template
就是组件将来要展示的内容 -
注意:不论是哪种方式创建出来的组件,组件的
template
属性指向的模板内容,必须有且只能有唯一的一个根元素。
15 组件里的data
- 组件可以有自己的
data
数据 - 组件的
data
和实例中的data
有点不一样,实例中的data
可以为一个对象,但是组件中的data
必须是一个方法。 - 组件中的
data
除了必须为一个方法之外,这个方法内部,还必须返回一个对象才行。 - 组件中的
data
数据,使用方式,和实例中的data
使用方式完全一样! - 组件里
data
为什么必须是个方法返回个对象呢? 因为要确保每个实例
里的数据是唯一的,独有的。如果data
里的数据是放在实例外部
的,会被其他实例
共享。
16 组件切换
- 组件里的切换 可以用
v-if
和v-else
进行切换 即标签页切换
<a href=""
@click.prevent="flag=true"
>
登录
</a>
<a href=""
@click.prevent="flag=flase"
>
注册
</a>
<login v-if="flag">
</login>
<register v-else="flag">
</register>
vue
提供了component
,来展示对应名称的组件
//component 是一个占位符
:is属性,可以用来指定要展示的组件的名称 写死的时候这个组件名要是个字符串,动态绑定时key普通写法就好,但value必须是字符串。
<component :is="'componentId'">
</component>
<component :is="oneName">
</component>
data(){
return{
oneName:"login",
}
}
17 父子组件通讯
-
父子组件传值,通过
v-bind:(:)
来传值,通过props
来接收值 -
父组件用事件绑定机制传递
方法
给子组件—v-on
简写@
//父组件中
<component-name
:children='children' //传值
@handle='show' //绑定方法
>
</component-name>
data(){
return(){
children:11
}
}
methods:{
show(data){
}
}
emit
英文原意: 是触发,调用,发射的意思。@handle=show
父组件传show
方法给子组件。 子组件接收父组件的方法,并用$emit
把子组件的值传给父组件
//子组件中
methods:{
handle(){
this.$emit('handle',{
age:1,
name:'搞事'
})
}
}
- 在父组件中接收子组件所有参数的同时,添加自定义参数
1.子组件传出单个参数时:
// 子组件
this.$emit('test',this.param)
// 父组件
@test='test($event,userDefined)'
2.子组件传出多个参数时:
// 子组件
this.$emit('test',this.param1,this.param2, this.param3)
// 父组件 arguments 是以数组的形式传入
@test='test(arguments,userDefined)'
3.可传出个对象
this.$emit('test',{param1:1,param1:2,param1:3})
tips:子组件中的
data
数据,并不是通过 父组件传递过来的,而是子组件自身私有的,比如子组件通过ajax
,请求回来的数据,都可以放到data
身上,data
上的数据都是可读可写的;
18 使用 ref 获取dom元素
<h3 id='myh3' ref='myh3'> </h3>
methods:{
getElement(){
console.log( this.$refs.myh3.innerText)
}
}
//组件也可以使用ref,让父组件调用子组件里的方法和属性值
<login ref='mylogin'> </login>
methods:{
getElement(){
//父组件调用子组件里的属性值
console.log(this.$refs.mylogin.msg)
}
}
tips:
refs
;s
代表多个引用,会有多个dom
元素。ref
英文是reference
,值类型和引用类型。
19 路由
1.这是vue-router
提供的元素,专门用来 当作占位符的,
将来,路由规则,匹配到的组件,就会展示到这个router-view
中去,所以:我们可以把router-view认为是一个占位符
<router-view></router-view>
2.路由切换 模板写法,默认渲染为一个a
标签,
使用tag
的span
可以用来转换模板的标签名
<router-link to="/login" tag='span' >
登录
</router-link>
3.路由配置
new VueRouter({
//路由匹配规则
routes:[
{
path:'/',
redirect:'/login'
},
{
path:'login',
component:login
},
{
path:'/register',
component:register
}
]
//路由高亮的类名
linkActiveClass:'myactive'
})
var vm=new Vue({
el:'#app',
data:{},
methods:{},
router //将路由规则对象注册到vm实例上,用来监听Url地址的变化,然后展示对应的组件。
})
4.路由传参
query
//跳转并传值
this.router.push({
name: 'TeacherDetail',//组件名
query: {
scaleId: 1
}
});
//在TeacherDetail组件中接收scaleId
this.$route.query.scaleId
- 在
path
上设置参数
//参数要一一对应不可缺失,不然可能会
造成路由的不匹配
<router-link to="/login/12/ls">
{
path:'/login/:id/:name',component:login
}
5.子路由
<router-link
to="/account/login"
>
</router-link>
routes:[
{
path:'/account',
component:account,
children:{
{
path:'login',
component:login
}
}
}
]
tips:
- 每个路由规则,都是一个对象,这个规则对象,身上,有两个必须的属性。
- 属性1 是
path
,表示监听,哪个路由链接地址;- 属性2是
component
,表示,如果路由是前面匹配到的path,则展示component
属性对应的那个组件。- 子路由不能加
/
, 加了/ 会以根目录为基准匹配,这样不方便我们用户去理解url
地址- 超链接的
to
一定要加上父路由
==注意==:componen
属性值,必须是一个组件的模板对象,不能是组件的引用名称
20 命名视图实现经典布局
根据name
来找组件
<router-view></router-view>
<router-view name="left"></router-view>
<router-view name="main"></router-view>
var header={
template:'<h1>header</h1>'
}
var leftBox={
template:'<h1>leftBox</h1>'
}
var mainBox={
template:'<h1>mainBox</h1>'
}
{
path:'/',components:{
'default':header,
'left':leftBox,
'main':mainBox
}
}
21 watch
监听非dom
元素
watch:{
'obj.a'(newValue,oldValue){ },
immediate:false
}
watch:{
'obj':{
handler (newValue, oldValue) {
}
},
deep:true //深程度监听 性能消耗大
}
watch:{
//监听路由
'$route.path':{
handler (newValue, oldValue) {
}
},
//immediate:true代表如果在 wacth 里声明了之后,就会立即先去执行里面的handler方法,如果为 false就跟我们以前的效果一样,不会在绑定的时候就执行
immediate:true
}
tips:
- 用了
obj.a
加上handle +immediate:true
,就可以监听 对象里的值,如果是obj
加上handle+deep true
也是可以监听对象的属性但是性能消耗大 一般是直接对象> > +属性- 用
handle
方法可以让watch
初始化就执行,如果不用 handle它就先不执行,待数据改变再执行。- 不要在
watch
和computer
中去修改参与计算或者监听的值 而是要生成新的值。
22 computed
计算属性
computed:{
'fullname':(){
return
}
}
23. render
函数注册组件(vm
[vue
实例]的属性)
render:function(createElements){
//createElements是一个方法,调用它,能够把指定的 组件模板 渲染为html结构
return createElements(login)
//注意 这里 return 的结果,会替换页面中el 指定的那个容器
}
tips:
render
和components
区别render
会把整个app
里组件全部覆盖掉一个app
中只能放一个render
组件components
可以多个,且不会覆盖
24. slot
插槽
1.写插槽
<div v-if="layout === 'block'" class="layout-block" :class="scroll?'layout-scroll':''">
<slot></slot> //匿名插槽
</div>
<!-- 左右块 -->
<div v-if="layout === 'both'" class="d-flex jc-between">
<div class="layout-both" :class="scrollLeft?'layout-scroll':''">
<slot name="left"></slot> //有名字的插槽
</div>
<div class="layout-both" :class="scrollRight?'layout-scroll':''">
<slot name="right"></slot>
</div>
</div>
2.使用插槽
//有名字的插槽 # v-slot的缩写是#
<template #left></template>
<template v-slot="left" > </template>
tips 区别对待v-slot="" 和v-slot:name; =和:的区别 一个是slot的name 一个是父组件获取子组件的数据,插槽一定要用 template包裹着
<template>
插槽的内容
</template>
杂项
1. nrm
安装 nrm
npm i nrm -g
全局安装nrm ls
显示列表nrm use npm
使用npm use
后有很多地址可选择
tips:
nrm
只是单纯的提供几个常用的下载包url
地址,并能够让我们在这几个地址之前,很方便的进行切换,但是,我们每次装包的时候,使用的装包工具,都是npm
。和
npm i cnpm -g 不一样
2. webpack
在网页中会引用哪些常见的静态资源
-
js
- .js .jsx .coffee .ts(TypeScript)
-
css
- .css .less .sass .scss
-
Image
- .jpg .png .gif .bmp .svg
-
字体文件(Fonts)
- .svg .ttf .eof .woff .woff2
-
模板文件
- .ejs .jade .vue
3. 热部署
webpack-dev-server
实现自动打包功能,浏览器不用刷新也能看到文件已经修改,打包的文件并没有放在实际的物理磁盘上,而是直接托管到了,电脑的内存中,所以,我们在项目根目录中,根本找不到这个打包好的文件,这文件和src
、 dist
、 node_modules
平级,有一个看不见的文件
4. 热更新
hot
网页不重载 直接更新 加快打包速度 不生成新文件
"scripts":{
"dev":"webpack-dev-ser ver --open --prot 3000 --contentBase src --hot "
}
配置文件中配置热更新
devServer:{
hot:true 就热更新了
}
tips:
webpack
当中带s
都是数组
5.webpack
引入vue
在 webpack
中 使用以下方式导入的Vue
构造函数,功能并不完善,只提供了 runtime-only
的方式,并没有提供 像网页中那样的使用方式;
//阉割版
import Vue from 'vue'
//齐全版
import Vue from '../node_modules/vue/dist/vue.js'
module.exports={
resolve:{
//设置 Vue被导入的时候的包的路径
alias:{
"vue$":"vue/dist/vue.js"
}
}
}
tips: 包的查找规则:
- 找项目根目录中有没有
node_modules
的文件夹- 在
node_modules
中 根据包名,找对应的vue
文件夹- 在
vue
文件夹中,找一个叫做package.json
的包配置文件- 在
package.json
文件中,查找一个main
属性[main
属性指定了这个包在被加载时候的入口文件]- 改了
webpake
的包就需要重新运行项目
6.在webpack
中通过render
展示组件
如果想要通过vue
,把一个组件放到页面中去展示,vm
实例中的render
函数可以实现
render:function(createElement){
return createElement(login)
}
//就一行可以省略{} 并且没有花括号默认就有return,
简写: render: c => c(login)
tips:
webpack
中 如何使用Vue:
- 安装
Vue
的包- 由于在
webpack
中,推荐使用.vue
这个组件模板文件定义组件,所以需要安装 能解析这种文件的loader
- 在
main.js
中,导入vue
模块import Vue from 'vue'
- 定义一个
.vue
结尾的组件,其中,组件有三部分组成- 使用
import login from './login.vue'
导入这个组件- 创建
vm
的实例var vm = new Vue
({el:'app',render:c=>c(login)})
- 在页面中创建一个
id
为app
的div
元素,作为我们vm实例要控制的区域
7. export default
和 export
-
export default
向外暴露的成员,可以使用任意的变量来接收 -
在一个模块中,
export default
只允许向外暴露1次 -
在一个模块中,可以同时使用
export default
和export
向外暴露成员 -
使用
export
向外暴露的成员,只能使用{}的形式来接收,这种形式,叫做[按需导出] -
export
可以向外暴露多个成员,同时,如果某些成员,我们在import
的时候,不需要,则可以不在 { }中定义 -
注意使用
export
导出的成员,必须严格按照导出时候的名称,来使用{ } 按需接收 -
使用
export
导出的成员,如果就想换个名称来接收,可以使用 as 来起别名
const arr={
a:'1',
b:'2'
}
export default arr
/* export default {
这个暴露是错误的所以当前注释 一个js文件中只能暴露一次
address:'北京'
}
*/
export title=1
import arr, {title as title1 } from '/xxx.js'
8. router
-
render
会把el
指定的容器中,所有的内容都清空覆盖,所有不要把路由的router-view
和router-link
直接写到el
所控制的元素中 -
注意
app
这个组件,是通过vm
实例的render
函数,渲染出来的,render
函数如果要渲染组件,渲染出来的组件,只能放到el: '#app'
所指定的元素中; -
Account
和GoodsList
组件,是通过路由匹配监听到的,所以,这两个组件,只能展示到属于路由的
<router-view></router-view>中去
- 子路由的应用场景在
标签页
切换
9. scoped
- 样式的
scoped
是通过css
的属性选择器来实现的,在vue
中的scoped
属性的效果主要通过PostCSS
转译实现,作用是实现了样式的模块化,防止样式全局污染。
.aa[vsfp]{
color:red
}
- 如何修改被scoped属性标识的样式 可以使用/deep/ 或 >>> 样式穿透,本人常用/deep/ 常用于修改自己封装的组件库或者三方的组件库
/deep/ .aa{
color:blue
}
tips:vsfp是哈希值
10. promise
- 模拟
promise
getFution(aa,callback){
callback(aa)
}
getFution(aa,funtion(res){
console.log (aa)
})
- 异步操作
/*
每当new一个Promise实例的时候,就会立即执行这个异步操作中的代码。也就是说,new的时候,除了能够得到一个promise实例之外,还会立即调用 我们为 Promise 构造函数传递的那个funtion,执行这个function中的异步操作代码。
可用一个函数包裹,这样就可以不立即执行,用函数执行
*/
var promise=new Promise(function(resolve,reject){
异步操作,ajax函数等,而且可以自由输出东西了
resolve(true)
reject(false)
})
getFunction(){
var promise=new Promise(function(){
异步操作,ajax函数等,而且可以自由输出东西了
})
}
getFunction().then(
function(data){
console.log(err)
},function(err){
console.log(err)
})
和async和await的结合
async getFunction(){
const aa= await getFunction()
console.log(aa)
}
tips:在
then
里面多写个返回错误的方法是不可取的。
11. vuex
-
vuex
是为了保存组件之间共享数据而诞生的,如果组件之间 有要共享的数据,可以直接挂载到vuex
中,而不必通过父子组件之间传值了,如果组件的数据不需要共享,此时,这些不需要共享的私有数据,没有必要放到vuex
中; -
vuex
,存放共享数据,data
存放组件私有的数据props
存放父组件传过来的数据 -
操作
vuex
里的state
- 直接操作
store
里的属性值,不推荐这种做法this.$store.state.属性值
- 推荐只通过
mutations
提供的方法,才能操作对应的数据,
mutations:{ increment(state,payload){ state.count++ } } this.$store.commit('方法名')
- 直接操作
-
vuex
里的getters
如果
store
中state
上的数据,在对外提供的时候,需要做一层包装,那么推荐使用getters
。optCount:state=>state.count this.$store.getters.***
tips:
increment
方法里的state
是属于vuex
里的state
;count
是state
里的属性 ;payload
外部传进来的值,用来修改state
里属性的值.。最多支持只能传两个参数,可以是数组和对象。
12. ngrok
可以映射本地80
端口,把本地的映射为外网
npm地址
//npm下载 --感觉下的有点慢 换个路径下比较好
npm install ngrok -g
//命令
ngrok http 80
tips:需要开启本地服务器,映射后只是映射www路径,不是完整的程序路径需要自己去补充完整,如果在服务器中(appach),有开启httpd-vhosts.conf,要用#关闭
#Include conf/extra/httpd-vhosts.conf
13.public 目录下的图片如何用 require引入
//第一种 图片质量小的可以自动转换为base64的
img: require("@/../public/img/home/user.jpg ")
//第二种 这里可以把最前面的 / 看做是public
/img/abnormal/Trash.png
即 public/img/abnormal/Trash.png
tips: @是指src目录 .. @的上一级目录,再进入public
14.发布订阅模式
可用于多个场景,比如兄弟组件间的数据传递,computer和watch 等无法监听的场景下
//订阅 异步的
this.$on('test', function (msg) {
console.log(msg) //输出hello word!
})
//发布
this.$emit('test', 'hello world!')
性能优化建议
-
watch
如果是obj 加上handle+deep true
也是可以监听对象的属性但是性能消耗大 -
computer
里生成的值 会有缓存 不建议用函数去处理一些值得计算 而是用computer
来计算值,这样性能高。 -
:key ='id'
id是列表返回的id 如果没有id 就写item 一般不建议 写 index (eslint会有警告错误信息) 写上key 是为了减少消耗 它会有个缓存。 -
v-once
和v-model
的区别是 只会绑定一次 不会重新更新内容 可以 减少开销 应用场景:只读场景,不进行修改页面内容的时候 -
v-for
和v-if
不适合在同个div连用
- 可在最外层套用一层
template
来解决
<template v-for="(column, index) in btn">
<el-button size="small"
:type='column.type'
:plain='column.plain'
:class='!isEmpty(column.style)&&[scope.$index + (currentPage - 1) * pageSize==column.style.index?column.style.className:""] '
@click="handle(id&&!column.isGetAll?scope.row[id]:scope.row,column.lable,scope.$index + (currentPage - 1) * pageSize)"
:key='index'>
{{column.lable}}
</el-button>
</template>
-
this.$parent
可修改父组件值,但不建议,只读就好 -
gzip
优化
vue
配置 在前端生成带有gz
的文件- 辅助插件:
compression-webpack-plugin
const CompressionWebpackPlugin = require('compression-webpack-plugin')
const productionGzipExtensions = ['js', 'css']
const isProduction = process.env.NODE_ENV === 'production'
configureWebpack: config => {
if (isProduction) {
config.plugins.push(
new CompressionWebpackPlugin({
algorithm: 'gzip',
test: new RegExp('\\.(' + productionGzipExtensions.join('|') + ')$'),
threshold: 10240,
minRatio: 0.8
})
)
}
},
ngnix服务端配置
//配合前端的gzip
在站点配置添加如下代码:
location ~* \.(css|js)$ {
gzip_static on;
}
这是 nginx 的静态 gzip功能,会自动查找对应扩展名的文件,如果存在 gzip 文件,就使用,如果没有就用原文件
//后端返回gzip
gzip on;
gzip_static on;
gzip_min_length 1k;
gzip_buffers 4 16k;
gzip_http_version 1.1;
gzip_comp_level 2;
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php application/vnd.ms-fontobject font/ttf font/opentype font/x-woff image/svg+xml;
gzip_vary on;
gzip_proxied expired no-cache no-store private auth;
gzip_disable "MSIE [1-6]\.";
cdn
加速
<script src="https://unpkg.com/vue@2.6.10/dist/vue.runtime.min.js"></script>
<script src="https://unpkg.com/vuex@3.0.1/dist/vuex.min.js"></script>
<script src="https://unpkg.com/vue-router@3.0.3/dist/vue-router.min.js"></script>
<script src="https://unpkg.com/axios@0.19.0/dist/axios.min.js"></script>
<script src="https://unpkg.com/element-ui@2.9.2/lib/index.js"></script>
//有了config就按以下配置
configureWebpack: config => {
//cdn
config.externals = {
vue: 'Vue',
vuex: 'Vuex',
'vue-router': 'VueRouter',
axios: 'axios'
}
if (isProduction) {
config.plugins.push(
new CompressionWebpackPlugin({
algorithm: 'gzip',
test: new RegExp('\\.(' + productionGzipExtensions.join('|') + ')$'),
threshold: 10240,
minRatio: 0.8
})
)
}
}
- 网页中引入的静态资源多了以后有什么问题?
- 网页加载速度慢, 因为我们要发起很多的
二次
请求; - 要处理错综复杂的依赖关系
解决方案:
- 合并、压缩、精灵图、图片的
base64
编码 、cdn
- 可以使用之前学过的
requireJs
、也可以使用webpack
- 建立不同的环境变量(开发、测试、正式)
一、建立环境配置文件
在package.json 同级的目录下 建立3个文件
1 .env.development --开发环境 (本地环境)
2 .env.production --正式环境 (正式线服务器--打包)
3 .env.test --测试环境 (测试线服务器--打包)
二、在每个文件中写入具体的配置内容
/*****.env.development文件的内容*****/
NODE_ENV = 'development'
VUE_APP_CURRENT_MODE = 'development'
/*****.env.production文件的内容*****/
NODE_ENV = 'production'
VUE_APP_CURRENT_MODE = 'production'
/*****.env.test*****/
NODE_ENV = 'production'
VUE_APP_CURRENT_MODE = 'test'
三、在package.json 中写入
1.在纯粹的vue_cli3.x配置如下
"scripts": {
"serve": "vue-cli-service serve --mode development",
"build": "vue-cli-service build --mode production",
"build:test": "vue-cli-service build --mode test",
},
2.在uni-app下的vue_cli3.x的配置
"scripts": {
"serve": "npm run dev:h5 -- development", //修改点
"build": "npm run build:h5 -- production", //修改点
"build:test": "npm run build:h5 -- test", //修改点
"build:h5": "cross-env NODE_ENV=production UNI_PLATFORM=h5 vue-cli-service uni-build --mode", //修改点
"build:mp-alipay": "cross-env NODE_ENV=production UNI_PLATFORM=mp-alipay vue-cli-service uni-build",
"build:mp-baidu": "cross-env NODE_ENV=production UNI_PLATFORM=mp-baidu vue-cli-service uni-build",
"build:mp-toutiao": "cross-env NODE_ENV=production UNI_PLATFORM=mp-toutiao vue-cli-service uni-build",
"build:mp-weixin": "cross-env NODE_ENV=production UNI_PLATFORM=mp-weixin vue-cli-service uni-build",
"dev:h5": "cross-env NODE_ENV=development UNI_PLATFORM=h5 vue-cli-service uni-serve --mode", //修改点
"dev:mp-alipay": "cross-env NODE_ENV=development UNI_PLATFORM=mp-alipay vue-cli-service uni-build --watch",
"dev:mp-baidu": "cross-env NODE_ENV=development UNI_PLATFORM=mp-baidu vue-cli-service uni-build --watch",
"dev:mp-toutiao": "cross-env NODE_ENV=development UNI_PLATFORM=mp-toutiao vue-cli-service uni-build --watch",
"dev:mp-weixin": "cross-env NODE_ENV=development UNI_PLATFORM=mp-weixin vue-cli-service uni-build --watch",
"info": "node node_modules/@dcloudio/vue-cli-plugin-uni/commands/info.js"
},
tips:以上不同环境的切换,修改点主要就是mode -- '环境变量'
各种插件的介绍和引用
- postcss-plugin-px2rem
配置文件名postcss.config.js
vue-cli3.x
脚手架自带的px
转rem
等单位和自动加浏览器前缀的配置,建立脚手架的时候选择分离,不然配置在josn
文件中不好配置; rootValue中的192为1rem所对应的px;默认为100,我这里设计图是1920的所以改为192。如果设计图是750,rootValue,就是75。
module.exports = {
plugins: [
//自动加前缀
require('autoprefixer')(),
//自动转rem
require('postcss-plugin-px2rem')({
rootValue: 192, //设计图的宽度/10
unitPrecision: 10, //换算的rem保留几位小数点
mediaQuery: flase,//媒体查询条件值是否开启转换rem
minPixelValue: 3
// exclude:/node_modules|folder_name/i,把第三方的框架排除掉
})
]
}
tips: 这边会有歧义,
mediaQuery
设置为true
,会导致@media screen and (max-width: 1200px){}
中的1200px
会转为rem
,但媒体查询内部的值的转换不会受到影响,会进行值的转换,所以设置为false
。
- babel-plugin-transform-remove-console
删除console
,在根目录中新建个.babelrc
的文件,在以下文件中配置
//第一种
{
"env": {
"production": {
"plugins": [
["transform-remove-console", { "exclude": ["error", "warn"] }]
]
}
}
}
//第二种
一、建立环境配置文件
在package.json 同级的目录下 建立3个文件
1 .env.development --开发环境 (本地环境)
2 .env.production --正式环境 (正式线服务器--打包)
3 .env.test --测试环境 (测试线服务器--打包)
二、在每个文件中写入具体的配置内容
/*****.env.development文件的内容*****/
NODE_ENV = 'development'
VUE_APP_CURRENT_MODE = 'development'
/*****.env.production文件的内容*****/
NODE_ENV = 'production'
VUE_APP_CURRENT_MODE = 'production'
/*****.env.test*****/
NODE_ENV = 'production'
VUE_APP_CURRENT_MODE = 'test'
三、在package.json 中写入
"scripts": {
"serve": "vue-cli-service serve --mode development",
"build": "vue-cli-service build --mode production",
"build:test": "vue-cli-service build --mode test",
},
四、在babel.config.js 中写
let transformRemoveConsolePlugin = [];
if (process.env.VUE_APP_CURRENT_MODE === "production") {
transformRemoveConsolePlugin = [
["transform-remove-console", { exclude: ["error", "warn"] }]
];
}
module.exports = {
presets: ["@vue/app"],
plugins: [...transformRemoveConsolePlugin]
};
html-webpack-plugin
当使用html-webpack-plugin
之后,我们不再需要手动处理bundle.js
的引用路径了,因为这个插件,已经帮我们自动创建了一个合适的script,并且,引用了正确的路径
/*导入在内存中生成html页面的插件,只要是插件,都一定要放到plugins节点中去
*/
const htmlWebpackPlugin=require("html-webpack-plugin")
//创建一个 内存中 生成html 页面的插件
new htmlWebpackPlugin({
template:path.join(__dirname,'./src/index.html')
filename:'index.html'
})
//这个节点,用于配置 所有 第三方模块 加载器
module:{
rules:[
{test:/\.css$,use:[]}
]
}
- prerender-spa-plugin
构建阶段生成匹配预渲染路径的 html 文件
npm install prerender-spa-plugin --save
vue.config.js
const PrerenderSPAPlugin = require('prerender-spa-plugin')
const Renderer = PrerenderSPAPlugin.PuppeteerRenderer
configureWebpack: config => {
if (isProduction) {
config.plugins.push(
new CompressionWebpackPlugin({
algorithm: 'gzip',
test: new RegExp('\\.(' + productionGzipExtensions.join('|') + ')$'),
threshold: 10240,
minRatio: 0.8
})
),
config.plugins.push(
new PrerenderSPAPlugin({
staticDir: path.join(__dirname, 'dist'),
routes: [
// '/'
'/login'
// '/show',
// '/websocket',
// 'websocket2',
// '/websocket3',
// '/home',
// 'abnormal/AbnormalStatis',
// 'abnormal/FocusCrowd',
// 'abnormal/FocusDetail',
// 'abnormal/ScaleDetail',
// 'abnormal/WarnSetup',
// 'abnormal/WarnDetail',
// 'abnormal/WarnLists',
// 'abnormal/PsychMonth',
// 'abnormal/PsychTeacher',
// 'abnormal/PsychList',
// 'laboratory/sports/MoveClock',
// 'laboratory/sports/ClockDetail',
// 'activity/ActList',
// 'activity/ActForm'
],
minify: {
minifyCSS: true, // css压缩
removeComments: true // 移除注释
},
server: {
port: 8080
},
//忽略打包错误
ignoreJSErrors: true,
phantomOptions: '--web-security=false',
maxAttempts: 10,
renderer: new Renderer({
injectProperty: '__PRERENDER_INJECTED',
inject: {
foo: 'bar'
},
headless: false,
renderAfterTime: 5000,
renderAfterDocumentEvent: 'render-event'
})
})
)
}
},
main.js
new Vue({
mounted() {
document.dispatchEvent(new Event('render-event'))
}
}).$mount('#app')
tips:
main.js
中的render-event
,要和renderAfterDocumentEvent:'render-event'
一一对应 ,publicPath
目前验证必须是/ 不能自定义文件夹