Vue项目经验
setInterval路由跳转继续运行并没有及时进行销毁
比如一些弹幕,走马灯文字,这类需要定时调用的,路由跳转之后,因为组件已经销毁了,但是setInterval还没有销毁,还在继续后台调用,控制台会不断报错,如果运算量大的话,无法及时清除,会导致严重的页面卡顿。
解决方案:在组件生命周期beforeDestroy停止setInterval
beforeDestory() {
clearInterval(this.timer);
MessageBox.close()
}
使用vue过程中你遇到了什么困难?
多级嵌套传参,解决思路自行百度
路由嵌套
路由嵌套会将其他组件渲染到该组件内,而不是进行整个页面跳转router-view本身就是将组件渲染到该位置,想要进行页面跳转,就要将页面渲染到根组件,在起始配置路由时候写到:
var App = Vue.extend({ root });
router.start(App,‘#app‘);
这里首先将根组件注册进来,用于将路由中配置好的各个页面渲染出来,然后将根组件挂载到与#app匹配的元素上。
组件的异步加载(按需加载组件)
在平时的demo中,你可能不会遇见这个需求,当页面很多,组件很多的时候,你会发现你的页面在首次加载的时候,异常的慢,这个是因为vue首次加载的时候把可能一开始看不见的组件也一次加载了,这个时候就需要对页面优化了,就需要异步组件了。如何去写异步组件呢,实际上很简单,只需要在你的路由index,js里加上require就可以了,像下面这样,这也是所谓的按需加载组件的实现原理。
vuejs构建组件使用
Vue.component(‘componentName‘,{ /*component*/ });
这里注意一点,组件要先注册再使用,也就是说:
Vue.component(‘mine‘,{
template:‘#mineTpl‘,
props:[‘name‘,‘title‘,‘city‘,‘content‘]
});
var v=new Vue({
el:‘#vueInstance‘,
data:{
name:‘zhang‘,
title:‘this is title‘,
city:‘Beijing‘,
content:‘these are some desc about Blog‘
}
});
如果反过来会报错,因为反过来代表先使用了组件的,但是组件却没注册。
webpack报错后,使用webpack --display-error-details可以排错
如何让css只在当前组件中起作用
在每一个vue组件中都可以定义各自的css,js,如果希望组件内写的css只对当前组件起作用,只需要在style中写入scoped,即:
<style scoped></style>
vuejs循环插入图片
在写循环的时候,写入如下代码:
<div class="bio-slide" v-for="item in items">
< img src="{{item.image}}">
</div>
此时在控制台会出现警告
[Vue Warn]: src="{{item.image}}": interpolation in "src" attribute will cause a 404 request. Use v-bind:src instead.这里意思是在“src”属性插值将导致404请求。使用v-bind:src代替。
所以替换成如下:
<div class="bio-slide" v-for="item in items">
< img v-bind:src="item.image">
</div>
这里需要主要,v-bind在写的时候不能再用{{}},根据官方的说法:
<a v-bind:href="url"></ a>
这里 href 是参数,它告诉 v-bind 指令将元素的 href 特性跟表达式 url 的值绑定。可能你已注意到可以用特性插值href="{{url}}" 获得同样的结果:这样没错,并且实际上在内部特性插值会转为 v-bind 绑定。
绑定value到Vue实例的一个动态属性上
对于单选按钮,勾选框及选择框选项,v-model绑定的value通常是静态字符串(对于勾选框是逻辑值):
<!-- `toggle` 为 true 或 false -->
<input type="checkbox" v-model="toggle">
但是有时候想绑定value到vue实例的一个动态属性上,这时可以用v-bind实现,并且这个属性的值可以不是字符串。例如绑定Checkbox的value到vue实例的一个动态属性:
<input
type="checkbox"
v-model="toggle"
v-bind:true-value="a"
v-bind:false-value="b">
<p>{{toggle}}</p >
这里绑定后,并不是说就可以点击后由true,false的切换变为a,b的切换,因为这里定义的动态a,b是scope上的a,b,并不能直接显示出来,此时
//当选中时
vm.toggle === vm.a
//当没选中时
vm.toggle === vm.b
所以此时需要在data中定义a,b,即:
new Vue({
el:‘...‘,
data:{
a:‘a‘,
b:‘b‘
}
});
实现多个根据不同条件显示不同文字的方法
v-if,v-else可以实现条件选择,但是如果是多个连续的条件选择,则需要用到计算属性computed。例如实现当输入框中什么都没写的时候显示字符串‘empty’,否则显示输入框中的内容,代码如下:
<div id="test">
<input type="text" v-model="inputValue">
<h1>{{changeVaule}}</h1>
</div>
new Vue({
el:‘#test‘,
data:{
changeVaule:‘123‘
},
computed :{
changeVaule:function(){
if(this.inputValue!==‘‘){
return this.inputValue;
}else{
return ‘empty‘;
}
}
}
});
Vuejs在变化检测问题
1.检测数组
由于javascript的限制,vuejs不能检测到下面数组的变化:
直接索引设置元素,如vm.item[0]={};
修改数据的长度,如vm.item.length。
为了解决问题1,Vuejs扩展了观察数组,为它添加一个$set()方法:
// 与 `example1.items[0] = ...` 相同,但是能触发视图更新
example1.items.$set(0, { childMsg: ‘Changed!‘})
问题2,需要一个空数组替换items。
除了$set(),vuejs也为观察数组添加了$remove()方法,用于从目标数组中查找并删除元素,在内部调用了splice()。因此,不必:
var index = this.items.indexOf(item)
if (index !== -1) {
this.items.splice(index, 1)
}
只需:
this.items.$remove(item);
2.检测对象
受ES5的显示,Vuejs不能检测到对象属性的添加或删除。因为Vuejs在初始化时候将属性转化为getter/setter,所以属性必须在data对象才能让Vuejs转换它,才能让它是响应的,例如:
var data = { a: 1 }
var vm = new Vue({
data: data
})
// `vm.a` 和 `data.a` 现在是响应的
vm.b = 2
// `vm.b` 不是响应的
data.b = 2
// `data.b` 不是响应的
不过,有办法在实例创建之后添加属性并且让它是响应的。对于Vue实例,可以使用$set(key,value)实例方法:
vm.$set(‘b‘, 2)
// `vm.b` 和 `data.b` 现在是响应的
对于普通数据对象,可以使用全局方法Vue.set(object, key, value):
Vue.set(data, ‘c‘, 3)
// `vm.c` 和 `data.c` 现在是响应的
有时你想向已有对象上添加一些属性,例如使用 Object.assign() 或 _.extend() 添加属性。但是,添加到对象上的新属性不会触发更新。这时可以创建一个新的对象,包含原对象的属性和新的属性:
// 不使用 `Object.assign(this.someObject, { a: 1, b: 2 })`
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
关于vuejs页面闪烁{{message}}
在vuejs指令中有v-cloak,这个指令保持在元素上直到关联实例结束编译。和CSS规则如[v-cloak]{display:none}一起用时,这个指令可以隐藏未编译的Mustache标签直到实例准备完毕。用法如下:
[v-cloak]{
display:none;
}
<div v-cloak>{{message}}</div>
这样<div>不会显示,直到编译结束
关于在v-for循环时候v-model的使用
有时候需要循环生成input,用v-model绑定后,利用vuejs操作它,此时我们可以在v-model中写一个数组selected[$index],这样就可以给不同的input绑定不同的v-model,从而分别操作他们。这个我在demo中的dataBind.vue中用到。
vuejs中过渡动画
在vuejs中,css定义动画:
.zoom-transition{
width:60%;
height:auto;
position: absolute;
left:50%;
top:50%;
transform: translate(-50%,-50%);
-webkit-transition: all .3s ease;
transition: all .3s ease;
}
.zoom-enter, .zoom-leave{
width:150px;
height:auto;
position: absolute;
left:20px;
top:20px;
transform: translate(0,0);
}
其中动画在定的时候要注意上下对应,上面有什么,下面有什么,都要变化的,如果有不变化的,应该抽离出去,作为公共css样式,在上面的css中,如果我只写 transform: translate(-50%,-50%);而不写下面的transform: translate(0,0);则会导致上面的transform: translate(-50%,-50%);被添加到下面,认为这个是不变的。
关于vuejs中使用事件名
在vuejs中,我们经常要绑定一些事件,有时候给DOM元素绑定,有时候给组件绑定。绑定事件在HTML中用v-on:click-"event",这时evet的名字不要出现大写,因为在1.x中不区分大小写,所以如果我们在HTML写v-on:click="myEvent"而在js中写myEvent就出错误,所以在vuejs的1.x绑定事件时候,要尽量避免使用大写字母。在2.0中没有该限制!
v-if与v-show的区别
v-if直接不渲染这个DOM元素,而v-show是会渲染DOM元素,只是使用display:none隐藏,打开开发者工具可以看到该DOM
关于transition全局钩子如何在组件中使用
Vue.transition是定义一个全局transition钩子的,如果想针对组件定义,则需要如下写法:
export default{
transition:{
‘fade‘:{
enter() {},
leave() {}
}
}
}
这样fade这个过度钩子只会作用于组件内,如果同时有同名的全局钩子,则会优先使用组建定义的
利用vue-router如何实现组件在渲染出来前执行某个事件
export default{
data(){
return{
selected:0,
currentView:‘view_0‘
}
},
methods:{
choose(index) {
this.selected=index;
this.currentView=‘view_‘+index;
}
},
route:{
data() {
/*每次切换路由,在渲染出页面前都会执行*/
}
}
}
Vue运行报错-。
[Vue-warn]: Missing required prop: "to" (found in component <router-link>) //报错1
这个错误是<router-link>少了个to或者是写错
解决: 正确写法为:<router-link to="/home">
路由在做字符串拼接的时候,to要作为一个属性绑定 <router-link :to="‘/home/‘+item.id">
-。 声明click/on-click的方法找不到
报错
[Vue warn]: Invalid handler for event "on-click": got undefined //报错1
解决: click/on-click的方法 没有写到methods:{ }里面。
给组件内的原生控件添加事件,不生效的问题
<!--比如用了第三方框架,或者一些封装的内置组件; 然后想绑定事件-->
<!--// 错误例子01-->
<el-input placeholder="请输入特定消费金额 " @mouseover="test()"></el-input>
<!--// 错误例子02-->
<router-link :to="item.menuUrl" @click="toggleName=‘‘">
<i :class="[‘fzicon‘,item.menuIcon]"></i>
<span>{{item.menuName}}</span>
</router-link>
<!--上面的两个例子都没法触发事件!!!-->
<!--究其原因,少了一个修饰符 .native-->
<router-link :to="item.menuUrl" @click.native="toggleName=‘‘">
<i :class="[‘fzicon‘,item.menuIcon]"></i>
<span>{{item.menuName}}</span>
</router-link>
<!--明明官方文档有的,一堆人不愿意去看,,Fuck-->
<!--https://cn.vuejs.org/v2/guide/components.html#给组件绑定原生事件-->1234567891011121314151617181920
-。
在函数内用了this.xxx=,为什么抛出 Cannot set property ‘xxx‘ of undefined;
这是this的套路了..this是和当前运行的上下文绑定的…
一般你在axios或者其他 promise , 或者setInterval 这些默认都是指向最外层的全局钩子.
简单点说:”最外层的上下文就是 window,vue内则是 Vue 对象而不是实例!”;
解决:
暂存法: 函数内先缓存 this , let that = this;(let是 es6, es5用 var)
箭头函数: 会强行关联当前运行区域为 this 的上下文;
关于this的知识, 推荐读阅 <<你不知道的 JS 系列>>
兼容问题
使用了 axios, IE 整个家族都不支持 promise,
解决方案:
npm install es6-promise
// 在 main.js 引入即可
require("es6-promise").polyfill(); // ES6的polyfill
终于做完了第一个vue项目,本项目前端采用Vue,后端采用express+mongodb,Vue是使用vue-cli生成的项目。
开发期的问题如下:
(1)最好使用cnpm代替npm安装依赖,因为开发过程中少部分包如果用npm是无法下载完成,一直卡住,换为cnpm毫无压力记得加上--save选项,否则别人安装的话会缺少包,linux下记得配置软连接才能使用cnpm命令
(2)express如何设置favicon: 其实默认express已经引入了serve-favicon包,只需要使用app.use(favicon(__dirname+你的favicon路径))即可
(3)百度地图的引入需要异步加载,否则会报错找不到BMap,具体见代码
(4)非父子组件通信新建一个Vue实例作为通信中转站,可以很方便的处理大部分非父子通信逻辑
(5)引入reset.css是在main.js中引入
(6)es6的import规则:如果路径最终是一个文件夹,则会首先观察文件夹下是否有 package.json ,如果有 package.json 则会去加载 main 字段指向的文件,如果没有 package.json ,则会在这个文件夹下寻找 index 文件并加载。所以main.js里面加载router直接这样写 import router from ‘./router‘
(7)为了处理某些浏览器兼容,比如ie,safari,必须引入babel-polyfill才行,在main.js里加上import ‘babel-polyfill‘,否则ie和safari会不认识es6的语法
(8)main.js里面Vue实例的template:"<App/>"的意思是将app组件替换掉#app的那个div,同时App也是一个组件,在components里面必须要声明
(9)vuex状态管理器的使用最好按照官网那一套来写,比较规范
(10)前端路由验证登录情况可以在路由里加上meta参数,如下图,然后router.beforeEach里判断requireAuth是否为真
(11)组件里面data使用图片路径是要用require(‘./../mm.png‘)才能正确访问图片,这样才能够被webpack正确解析
(12)下拉框最好不用原生的,不同浏览器表现不同,得自己写一套才行
(13)server端express自带前端部分,在views里面,注意设置engine解析,是html的引擎,引入ejs包
app.set(‘views‘, path.join(__dirname, ‘views‘));
app.engine(‘.html‘,ejs.__express);
app.set(‘view engine‘, ‘html‘);
(14)开发环境下我的项目路径下有中文,结果jieba分词包执行的时候报错:找不到jieba.node,然后将路径改为英文问题解决,得出结论路径别加中文,指不定啥问题会出现
部署服务器出现的问题:
(1)首先express默认设置的是3000端口,所以多个项目部署的话得修改bin/www里面的port值,改为不一样的
(2)线上部署首先得把server文件夹内的所用内容拷贝到阿里云服务器上,新建一个文件夹来存放。阿里云服务器必须得设置安全组,目的是将开放访问的端口,否则端口无法访问,比如express设置了4000端口,阿里云这里就得设置4000端口开放
(3)服务器环境搭建:首先安装node,然后安装mongodb,注意服务器上的mongodb必须设置访问权限,否则非常不安全,同时server端连接mongo的语句也得加上用户名和密码
(4)代码拷贝后执行cnpm install 安装依赖,安装完成node www启动项目,此时遇到问题如下图,可以看出此时的node不支持async函数,也就是说node版本太低了,但是我本地测试没问题,查询node版本后发现,本地node是v8.3.0,服务器上是v6.4,因此要升级node,具体方法为:1,安装n模块 npm install -g n,这个模块专门管理node版本的,2,n stable此命令可升级到最新稳定版,n v8.3.0这个命令可升级到指定版本。我将服务器上的node升级到和本地一样后问题解决了
(5)由于本项目使用了selenium-webdriver自动化测试工具当做爬虫(原因是某些网站主内容在iframe内,一般爬虫无法获取内容,selenium内有switchToFrame方法切换frame从而获取到内容),本地测试时这个工具运行时会自动打开chrome(事先设置了chrome参数),但是放到阿里云服务器上时报错找不到chrome binary,查询后发现必须安装chrome浏览器,具体链接http://www.linuxidc.com/Linux/2016-05/131096.htm 然后继续启动发现仍然报错:chrome failed to start. 仔细分析后发现这是由于没有设置headless参数导致的,由于阿里云服务器没有界面,必须设置启动方式为headless无头,同时记得添加参数no-sandbox,不添加则chrome仍然无法启动,以下代码在官网上在找到的。
(6)前端打包后将dist内的文件拷贝到server里面views目录下即可,然后启动express即可访问页面,但是页面里面出现许多png not found,检查后发现是路径问题,这些找不到的png的路径都是写在css里面,由于打包后路径发生改变,所以找不到。解决方法是修改前端build/util.js 里面的路径,添加publicPath参数
未解决的问题如下:
(1)开发环境下访问第三方网站必须做跨域处理,为啥生产环境下可以不用跨域?直接这样写就行
(2)
vue 项目搭建
前言
基于Vue.js 2.x系列 + Element UI 的组件化开发方案,vue.js不支持IE8及其以下版本,因为 Vue.js 使用了 IE8 不能模拟的 ECMAScript 5 特性。 Vue.js 支持所有兼容 ECMAScript 5 的浏览器。vue的安装依赖于node.js,要确保你的计算机上已安装过node.js。可进入cmd编辑器,输入命令 node -v进行查看。node尽量要用新一些的版本,否则后续安装会提示node版本过低。去node官网下个新版的node重新安装就可以。
安装步骤
1.全局安装node.js
下载地址:https://nodejs.org/en/download/
安装成功后可进入cmd编辑器,输入命令 node -v进行查看,出现版本号说明安装成功,npm是包管理工具,noed安装好就有npm指令了
C:\Users\user>node -v
v8.4.0
C:\Users\user>
2.全局安装webpack 和 vue-cli
npm install -g webpack
npm install -g vue-cli
3.根据vue-cli构建项目
vue init webpack my-vue //创建一个项目,名称my-vue1
cd my-vue // cd 到my-vue的根目录下1
npm install // 下载项目依赖1
npm run dev // 让该项目在本地上跑起来1
这样一个基础的vue项目目录就自动会展现在你面前,我们可以来看一下其自动生成的基础文件:
项目结构简单介绍
├── build // 是一些webpack的文件,配置参数什么的,一般不用动
│ ├── build.js
│ ├── check-versions.js
│ ├── dev-client.js
│ ├── dev-server.js
│ ├── utils.js
│ ├── vue-loader.conf.js
│ ├── webpack.base.conf.js
│ ├── webpack.dev.conf.js
│ └── webpack.prod.conf.js
├── config // 环境配置文件,是vue项目的基本配置文件
│ ├── dev.env.js
│ ├── index.js
│ └── prod.env.js
├── node_modules // npm包文件 是项目中安装的依赖模块
├── src // 源码文件夹,基本上文件都应该放在这里。
│ ├── assets //资源文件夹,里面放一些静态资源
│ │ └── logo.png
│ ├── components //这里放的都是各个组件文件
│ │ └── Hello.vue
│ ├── router
│ │ └── index.js
│ ├── App.vue //App.vue组件
│ └── main.js //入口文件
├── static //生成好的文件会放在这个目录下。
├── .babelrc // babel配置文件 , vue开发需要babel编译
├── .gitignore // gitignore忽略文件
├── .editorconfig // 编码风格配置文件
├── .postcssrc.js // postcss配置文件
├── package.json // node包管理文件
├── index.html // 首页模板
├── package.json // 包管理文件
└── README.md // 描述文件123456789101112131415161718192021222324252627282930313233
main.js文件介绍
这里是入口文件,可以引入一些插件或静态资源,当然引入之前要先安装了该插件,在package.json文件中有记录。
/*引入Vue框架*/
import Vue from ‘vue’
/*引入资源请求插件*/
import VueResource from ‘vue-resource’
/*重置样式*/
import “assets/css/base.css”
/*基本JS*/
import “assets/js/common.js”
/*引入路由设置*/
import “./routers.js”
/*使用VueResource插件*/
Vue.use(VueResource)
App.vue
这是一个标准的vue组件,包含三个部分,一个是模板,一个是script,一个是样式,这里需要了解vue的基础。
import Hello from ‘./components/Hello‘
export default {
name: ‘app‘,
components: {
Hello
}
}
app {
font-family: ‘Avenir’, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
vue-route
作用:
通过管理url实现url和组件的对应,
通过url进行组件之间的切换
必须引入router组件 是单独文件
Hello App!
Go to Foo
Go to Bar
axios使用
## 安装 ##
npm install axios vue-axios -D
在main.js 入口文件里面写
import Axios from ‘axios’
import VueAxios from ‘vue-axios’
Vue.use(VueAxios,Axios)
在其它组件里面调用
Javascript
export default {
name: ‘blog‘,
created () {
this.getGoods()
},
methods: {
getGoods () {
this.$http.get(‘http://lc.shudong.wang/api_goods.php‘)
.then((res) => {
console.log(res.data)
})
.catch((error) => {
console.log(error)
})
}
}
}
## vue UI组件
使用目的:
提高开发效率
直接拿过来用
elementUi
安装
npm ielement-ui -D
## 引入 main.js 入口文件 ##
import ElementUI from ‘element-ui‘
import ‘element-ui/lib/theme-default/index.css‘
Vue.use(ElementUI)
hipv2头痛的问题
1、先操作element分页,切换到第2页
模拟画面数据重新查询,从代码里面将current-page强制设置为1(即重新查询数据,并将当前页重置为第1页)
此时画面显示是对的,分页组件已经将第1页的页码数字激活了
然后点击第2页数字,进行换页
画面显示也是对的,第2页数字变成激活状态,但是此时竟然无法触发current-change事件
个人猜测,其内部逻辑也许还认为当前页是第2页,所以没有再次触发current-change
这样会导致数据重新加载后,分页出现bug,无法加载分页数据
解决分页BUG的办法
1、使用v-if强制刷新组件即可组件声明改成:(data里面初始化paginationShow为true)
<el-pagination
v-if="paginationShow"
:current-page.sync ="currentPage"
background @current-change="pageChange"
:page-size="perPage"
layout="total, prev, pager, next"
:total="total">
</el-pagination>
2、把search方法改成下面这样:
search () {
this.paginationShow = false
// 模拟ajax查询数据处理,查询成功后执行下面代码
this.total = res.data.total;
this.perPage = res.data.pageSize;
this.$nextTick(function () {
this.paginationShow = true
})
}
3、注意事项:
paginationShow = true,重新显示分页,必须放到$nextTick,画面刷新完毕后,不然无效果1234567891011121314151617181920212223242526272829
后期项目优化
第一次使用vue2.0开发,在使用vue-cli脚手架打包后(UI用的Element-ui)(请求模块用的时axios),发现vendor文件很大,2M左右。。后来翻阅资料才明白,原来webpack把所有的库都打包到了一起,导致文件很大。
我的优化方法:
方法1:
1.把不常改变的库放到index.html中,通过cdn引入,比如下面这样:
<!DOCTYPE html>
<html lang="en">
<head>
<!-- <meta base="/MBSS/"> -->
<meta charset="UTF-8">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0;"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta http-equiv="Access-Control-Allow-Origin" content="*" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="shortcut icon " type="images/x-icon" href="/static/img/bitbug_favicon.ico">
<!-- <link href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1" rel="stylesheet" type="text/css"> -->
<title>和佳医院信息集成平台</title>
<!-- 先引入 Vue -->
<!--开发环境-->
<script src="https://cdn.bootcss.com/vue/2.5.16/vue.js"></script>
<!--生产环境-->
<!--<script src="https://cdn.bootcss.com/vue/2.5.16/vue.min.js"></script>-->
<!-- 引入组件库 -->
<script src="https://cdn.bootcss.com/axios/0.15.3/axios.min.js"></script>
<script src="https://cdn.bootcss.com/element-ui/2.3.3/index.js"></script>
<script src="https://cdn.bootcss.com/echarts/4.0.4/echarts-en.common.js"></script>
<script src="./static/js/config.js"></script>
</head>
<body>
<div id="app"></div>
</body>
</html>12345678910111213141516171819202122232425262728
2.然后找到build/webpack.base.conf.js文件,在 module.exports = { } 中添加以下代码:
externals:{
‘vue‘: ‘Vue‘,
‘element-ui‘: ‘ELEMENT‘,
‘echarts‘:‘echarts‘,
‘axios‘:‘axios‘
}123456
3.在mian.js中import … from …就可以去掉了,若没去掉webpack还是会把对应的依赖进行打包。
这样webpack就不会把vue.js, echarts, element-ui, axios库打包了。声明一下,我把main.js中对element的引入删掉了,不然我发现打包后的app.css还是会把element的css打包进去,删掉后就没了。
然后你打包就会发现vendor文件小了很多~
如果你还不满足,请接着往下看·····
方法2:
vue路由的懒加载(具体作用,官网查看,这里就不多介绍了)。
刚开始我们使用路由可能是下面这样(router.js),这样一开始进入页面就会把所有的路由资源都加载,如果项目大,加载的内容就会很多,等待的时间页就会越长,导致给用户的不好的体验效果。
import login from ‘../components/login/login.vue‘
import Index from ‘../components/stystemm/index/index.vue‘
import home from ‘../components/stystemm/home/home.vue‘
import ImageList from "../components/stystemm/ImageList/ImageList.vue"
export default new Router({
routes:[
{
path:‘/‘,
component: hospital
},
{path:‘‘,component:home},
{path:‘/imageList‘,component:ImageList}
}
]
})12345678910111213141516
为了把路由分模块,然后每次进入一个新页面才加载该页面所需要的资源(也就是异步加载路由),我们可以像下面这样使用(router.js):
1.vue异步组件写法:
export default new Router({
routes: [{
path: ‘/‘,
name: ‘Login‘,
component: resolve => require([‘../components/page/Login.vue‘],resolve) ,
},
{
path: ‘/empi‘,
name: ‘eMPI管理‘,
component: resolve => require([‘../components/common/index.vue‘],resolve),
children: [{
path: ‘/empi/summary‘,
name: ‘索引总览‘,
component: resolve => require([‘../components/page/eMPI/pandect/pandect.vue‘],resolve)// empi - 总览
},
{
path: ‘/empi/patient‘,
name: ‘患者信息‘,
component: resolve => require([‘../components/page/eMPI/PatientInformation/PatientInformation.vue‘],resolve)
}
]
},
{
path: ‘/role‘,
name: ‘404‘,
component:resolve => require([‘../components/page/404.vue‘],resolve)
}
]
});
123456789101112131415161718192021222324252627282930
2.es提案的import()写法:
// 下面2行代码,没有指定webpackChunkName,每个组件打包成一个js文件。
/* const Home = () => import(‘@/components/home‘)
const Index = () => import(‘@/components/index‘)
const About = () => import(‘@/components/about‘) */
// 下面2行代码,指定了相同的webpackChunkName,会合并打包成一个js文件。 把组件按组分块
const Home = () => import(/* webpackChunkName: ‘ImportFuncDemo‘ */ ‘@/components/home‘)
const Index = () => import(/* webpackChunkName: ‘ImportFuncDemo‘ */ ‘@/components/index‘)
const About = () => import(/* webpackChunkName: ‘ImportFuncDemo‘ */ ‘@/components/about‘)
{
path: ‘/about‘,
component: About
}, {
path: ‘/index‘,
component: Index
}, {
path: ‘/home‘,
component: Home
}12345678910111213141516171819
3.webpack提供的require.ensure() vue-router配置路由,使用webpack的require.ensure技术,也可以实现按需加载。这种情况下,多个路由指定相同的chunkName,会合并打包成一个js文件。
{
path: ‘/home‘,
name: ‘home‘,
component: r => require.ensure([], () => r(require(‘@/components/home‘)), ‘demo‘)
}, {
path: ‘/index‘,
name: ‘Index‘,
component: r => require.ensure([], () => r(require(‘@/components/index‘)), ‘demo‘)
}, {
path: ‘/about‘,
name: ‘about‘,
component: r => require.ensure([], () => r(require(‘@/components/about‘)), ‘demo-01‘)
}12345678910111213
方法3:
在项目config/index.js中可以开启gzip压缩,对打包优化也有很大的帮助
1.首先安装插件 compression-webpack-plugin
cnpm install --save-dev compression-webpack-plugin1
2.设置productionGzip: true
// Gzip off by default as many popular static hosts such as
// Surge or Netlify already gzip all static assets for you.
// Before setting to `true`, make sure to:
// npm install --save-dev compression-webpack-plugin
productionGzip: true,
productionGzipExtensions: [‘js‘, ‘css‘],
// Run the build command with an extra argument to
// View the bundle analyzer report after build finishes:
// `npm run build --report`
// Set to `true` or `false` to always turn it on or off
bundleAnalyzerReport: process.env.npm_config_report123456789101112
3.npm run build执行后会发现每个js和css文件会压缩一个gz后缀的文件夹,浏览器如果支持g-zip 会自动查找有没有gz文件 找到了就加载gz然后本地解压 执行。
---------------------
首页头部栏的搜索功能,全局搜索。
直接跳转查询界面,然后这个很简单,输入框回车事件push到查询界面,然后将值一块儿带过去,初始化查询界面。
可是问题来了,现在查询这个组件里继续输入然后回车。这时候路由是没变化的,组件里的钩子也不会再次执行。怎么监测到搜索内容变化呢。
首先想到的是监测路由数据变化,后来发现太low,
再次想到的是监测vuex数据,搞了半天还是不行。
最后使用了非父子组件传值,
建立一个bus文件,文件建一个bus.js
内容就是引入vue,建立一个空的vue实例。
在头部搜索栏的组件里引入bus,用bus.$emit()触发一个事件传值,也就是搜索框的回车时触发。在查询界面引入bus,之后bus.$on()得到数据变化。然后做出相应的操作。
这里要注意一定不能使用bus.$(‘value‘,function(val){});因为这样的话,你的函数里的this会指向空实例bus。所以我们必须使用es6的写法,val => {};这样的话this会指向当前查询界面的实例。
vue打印功能,window.open(url}这个东西很多bug。第一用户体验,用户必须允许否则就无法弹出,第二响应时间超过一定时间则无法弹出。
解决方法:
跳转一个指定界面,然后重定向。
var newWindow= window.open(‘‘)
setTimout(()=>{
newWindow.location.href = ‘新的url‘
},3000);
软删除数据如何过滤
我们的网站删除如果是后台数据库已有数据是不能硬删除的,只能修改某个字段然后标记为删除,叫做软删除。
这样的话我们的前端界面渲染时就需要过滤掉你刚刚删除的数据
我的项目里是用的table然后给了tableData | filterData
filterData:
var newData = [];
for(let key of tableData){
if(key.removed !== 1){ newData.push(key) }
return newData
}
简单解释:就是全局过滤器获取原数据,然后如果某个字段是已经删除了,也就是软删除改变了字段的数据,就不管他了,其他的就生成一个新数组然后返回。
共用界面如何刷新
项目里一个费用申报单界面是共用的,很多地方过来查看和编辑也是这个路由这个组件,只是很多input的内容是空或者是不能编辑只能看。
问题来了,你来到这个界面后查看完了突然想我想现在编辑一个申报单,可是此时此刻你点击路由这个组件是不会更新的,因为vue觉得你已经在这个界面了,他是不会理你的。
使用过几个路由钩子,什么routerberfor……,什么放在app.vue里,都只能检测到路由离开进来的变化,而无法就在当前路由点击当前路由的辩护。
解决方法使用watch 监测路由
//监视路由变化,然后初始化界面。
watch: {
‘$route‘ (to, from) {
alert(‘路由改变了‘);
}
}
路由配置:
path:urlal
不管是插件还是你自己写的头部菜单,都一定有一个事件监听到你点击当前路由。在这个事件里我们来处理
let val = new Date();
this.$router.push({
path: url/val
})
这样你就可以在你的组件里监听到路由变化了。然后也就可以做出相应的操作。原理就是利用参数的变化呗,时间new出来的永远都是不一样的。
表单重置
项目中很多时候界面的数据不需要清除,但是一些操作会触发验证事件,然后一串红字你不达到条件就不会消失,这时候很难受,所以就有了重置表单的验证。
关于表单重置,我使用的是element-ui自带的resetFields()事件。
我们唯一要注意的是,不能再created()钩子中使用,因为this.$refs[formname]中的ref,一定要加载完后才能找到的。否则会报错说你resetFields()事件未定义。所以一定要在mounted()钩子中使用。
require(‘!style-loader!css-loader!less-loader!./assets/css/common_m.less‘)
PS:在webpack中配置好了loader之后,不需要在引入文件时写loader和文件后缀。
cssloader:
{
test: /\.css$/,
exclude: /node_modules/,
loader: "style-loader!css-loader",
},
{
test: /\.less$/,
exclud: /node_module/,
loader: "style-loader!css-loader!less-loader",
},
// <router-view></router-view>与写的位置无关
path.join(__dirname,‘..‘);
this.$set(this, ‘属性‘, ‘值‘)
css scoped 仅本组件
对象可以存图片。使用require即可。
images: {
error: require(‘./toast-error.png‘),
success: require(‘./toast-success.png‘),
load: require(‘./toast-load.gif‘),
waiting: require(‘./toast-hourglass.svg‘)
}
border。各屏幕分辨率不同出现的粗细问题。
公共部分。写成组件。利用传参改变样式/内容。
Ajax。axios。
跨域问题的解决:1.服务器端协助配置。2.jsonp。3.devServer配置代理。
能用data数据解决的问题,不用方法解决。如取反。长度。是否空/false/true。(Vue是数据驱动而不是结构驱动)
v-bind:style src to ...
路由传参。$route。
历史记录切换。router-link to/:to或者router.push()/router.replace()
#router.push(location) 导航到不同的 URL
// 声明式
<router-link :to="...">
// 编程式
router.push(...)
// 字符串
router.push(‘home‘)
// 对象
router.push({ path: ‘home‘ })
// 命名的路由
router.push({ name: ‘user‘, params: { userId: 123 }})
// 带查询参数,变成 /register?plan=private
router.push({ path: ‘register‘, query: { plan: ‘private‘ }})
#router.replace(location) 它不会向 history 添加新记录,而是替换掉当前的 history 记录。
// 声明式
<router-link :to="..." replace>
// 编程式
router.replace(...)
#router.go(n) 在 history 记录中向前或者后退多少步,类似 window.history.go(n) 值可以正可以负
#综合使用
const router = new VueRouter({
routes: [
{
path: ‘/user/:userId‘,
name: ‘user‘,
component: User
}
]
})
<router-link :to="{ name: ‘user‘, params: { userId: 123 }}">User</router-link>
router.push({ name: ‘user‘, params: { userId: 123 }})
这两种方式都会把路由导航到 /user/123 路径。
// 命名视图
// https://router.vuejs.org/zh-cn/essentials/named-views.html
// https://jsfiddle.net/posva/6du90epg/
// 有时候想同时(同级)展示多个视图,而不是嵌套展示,例如创建一个布局,有 sidebar(侧导航) 和 main(主内容) 两个视图,这个时候命名视图就派上用场了。
// 你可以在界面中拥有多个单独命名的视图,而不是只有一个单独的出口。如果 router-view 没有设置名字,那么默认为 default。
<router-view class="view one"></router-view>
<router-view class="view two" name="a"></router-view>
<router-view class="view three" name="b"></router-view>
// 一个视图使用一个组件渲染,因此对于同个路由,多个视图就需要多个组件。确保正确使用 components 配置(带上 s):
const router = new VueRouter({
routes: [
{
path: ‘/‘,
components: {
default: Foo,
a: Bar,
b: Baz
}
}
]
})
重定向 和 别名
切换时清空历史记录。数据丢失。sessionStorage。
相对路径。
append。concat。
渲染成某个标签。tag
可以在router-link内嵌套其他内容。
类型都是Object
$route.params(一个 key/value 对象,包含了 动态片段 和 全匹配片段,如果没有路由参数,就是一个空对象。)
$route.query(一个 key/value 对象,表示 URL 查询参数。例如,对于路径 /foo?user=1,则有 $route.query.user == 1,如果没有查询参数,则是个空对象。)
<div>
<router-link :to="{ name: ‘Hello‘ , params: { userId: 123 }}">
click1
</router-link>
</div>
<div>
<router-link :to="{ path: ‘hello‘ , query: {show: 2}}">
click2
</router-link>
</div>
console.log(this.$route.params.userId)
console.log(‘top is params, bottom is query‘)
console.log(this.$route.query.show)
watch:
watch: {
‘$route‘ (to, from) {
// 对路由变化作出响应...
}
}
v-show和v-if 。v-if为假直接不渲染。v-show为假display none。
当 v-if 与 v-for 一起使用时,v-for 具有比 v-if 更高的优先级。
个人建议:外容器overflow hidden,内容器overflow scroll。
过渡效果:
<transition name="fade"></transition>
.fade-enter-active, .fade-leave-active {
-webkit-transition: opacity .7s;
transition: opacity .7s;
}
.fade-enter, .fade-leave-active {
opacity: 0
}
路由钩子:判断从哪个路由过来,到哪个路由。然后跳转到哪个路由。(全局钩子。局部钩子。)
component页面内也可以监听路由。
滚动行为。历史记录之间切换。页面位置。返回对象。
scrollBehavior (to, from, savedPosition) {
return { x: 0, y: 0 }
}
锚点的实现。通过scrollTop。或滚动行为。
路由元信息。路由嵌套。
懒加载。
在异步加载页面中载嵌入异步加载的组件时对页面是否会有渲染延时影响?
答:会, 异步加载的组件将会比页面中其他元素滞后出现, 页面会有瞬间闪跳影响;
解决方案:因为在首次加载组件的时候会有加载时间, 出现页面滞后, 所以需要合理的进行页面结构设计, 避免首次出现跳闪现象;
At all:
1、路由页面以及路由页面中的组件全都使用懒加载
优点:(1)最大化的实现随用随载
(2)团队开发不会因为沟通问题造成资源的重复浪费
缺点:(1)当一个页面中嵌套多个组件时将发送多次的http请求,可能会造成网页显示过慢且渲染参差不齐的问题
2、路由页面使用懒加载, 而路由页面中的组件按需进行懒加载, 即如果组件不大且使用不太频繁, 直接在路由页面中导入组件, 如果组件使用较为频繁使用懒加载
优点:(1)能够减少页面中的http请求,页面显示效果好
缺点:(2)需要团队事先交流, 在框架中分别建立懒加载组件与非懒加载组件文件夹
3、路由页面使用懒加载,在不特别影响首页显示延迟的情况下,根页面合理导入复用组件,再结合方案2
优点:(1)合理解决首页延迟显示问题
(2)能够最大化的减少http请求, 且做其他他路由界面的显示效果最佳
缺点:(1)还是需要团队交流,建立合理区分各种加载方式的组件文件夹
export default new Router({
routes: [
{
path: ‘/‘,
component: resolve => require([‘components/Hello.vue‘], resolve)
},
{
path: ‘/about‘,
component: resolve => require([‘components/About.vue‘], resolve)
}
]
})
// solve:
// components/page
<keep-alive> 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和 <transition> 相似,<keep-alive> 是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在父组件链中。
当组件在 <keep-alive> 内被切换,它的 activated 和 deactivated 这两个生命周期钩子函数将会被对应执行。
主要用于保留组件状态或避免重新渲染。
组件:
当注册组件(或者 props)时,可以使用 kebab-case ,camelCase ,或 TitleCase 。Vue 不关心这个。
component命名要求:
1. 检查名称是否与 HTML 元素或者 Vue 保留标签重名,不区分大小写。(常用HTML标签)
2. 检查组件名称是否以字母开头,后面跟字母、数值或下划线。
使用 Virtual DOM 解析模板时,不会将模板中的标签名转成小写,而是保留原始标签名。然后,使用原始的标签名进行匹配组件。
例如, <MyComponent></MyComponent> 不会转为为小写形式,直接以 MyComponent 为基础开始匹配。
当然,匹配的规则依次匹配:原标签名、camelCase化的标签名、PascalCase化的标签名。
computed,内容变时会更新,否则会缓存起来。
如果是传参等方式得到值,需要判断值是否为空,否则会报值为undefined的错。
vue项目中,不用写CSS的兼容性代码。
因为vue-loader在编译.vue文件的时候,使用了Postcss的工具,它会给有兼容性问题的属性添加兼容性代码。
它是根据can i use官网写的代码。
写在<style></style>内才会生效。在html中添加style属性是不会添加兼容性代码的。
换行问题:
v-html可以直接加<br>进行换行,v-model中无效
例外:textarea标签使用v-model时,可以使用\r进行换行
ref的使用:
在父页面调用子组件的标签:
在父页面中的组件上加ref。然后在子组件中的某个标签加ref。即可。
nextTick
将回调延迟到下次 DOM 更新循环之后执行。
https://cn.vuejs.org/v2/api/#vm-nextTick
new Vue({
// ...
methods: {
// ...
example: function () {
// 修改数据
this.message = ‘changed‘
// DOM 还没有更新
this.$nextTick(function () {
// DOM 现在更新了
// `this` 绑定到当前实例
this.doSomethingElse()
})
}
}
})
box-sizing: border-box
:disabled="type == ‘值‘"
:class = name
name可以是compute
getBoundingClientRect用于获取某个元素相对于视窗的位置集合。集合中有top, right, bottom, left等属性。
点击事件。取消冒泡。
v-on:click.stop="doThis"
阻止默认事件。
v-on:click.prevent="doThis"
https://cn.vuejs.org/v2/guide/events.html#事件修饰符
组件内部的方法,命名建议以两个下划线开头
外部传参方法等,不用两个下划线开头。
padding取%值,
> %,规定基于父元素的宽度的百分比的内边距。
给按钮加padding。方便点击
或者整个部分可以点击。
export 和 export default
http://es6.ruanyifeng.com/#docs/module#export-命令
http://es6.ruanyifeng.com/#docs/module#export-default-命令
切换组件时,希望保留组件的状态时,加上keep-alive 即可
个人做vue项目的心得和体会
vue.js
首先,vue.js是一个数据驱动视图的一个js框架,操作数据,然后实时反应到dom元素上的一个动态视图框架,它也是一个渐进式开发框架,比如,我用vue-cli搭建了一个本地开发环境,用nodejs跑了起来,当把vue引进来之后,要做项目还是远远不够的,就用到了vue全家桶
比如
UI框架 : element-ui
http请求工具:axios(个人推荐用这个)
cookie工具:vue-cookie
构建工具:vue-cli
路由插件:vue-router
状态管理工具:vuex
滚动条插件:vuescroll
兼容IE的插件:babel-polyfill(当然vue只兼容到IE9+)
如果平时项目中你用到了哪些框架及插件,就可以通过npm install,下载下来,引入到项目中去就ok了
个人觉得项目目录结构可以是这个样子的
主要是src文件夹,assets主要用于放一些项目中的图片等之类的,compoents就放一些自己定义的组件,pages就放页面,当然都是vue文件啦,router放路由配置,server放一些静态目录什么的,放一些常量什么的都可以,store,熟悉vuex的小伙伴应该都知道,没错,就是放vuex的
通过用vue做了一些东西后,引发了我的一些思考,也算是心得吧,跟我写代码的风格有关系,也许你觉得我说的这些不好,你可以继续延用你的风格,哈哈,毕竟敲代码都有自己独特的风格嘛,哪个程序猿还没有点傲娇的小脾气呢!
关于使用UI框架
一般我都是用element-ui这个ui框架,其中有一些ui组件特别好用,比如$message,提示组件,不用自己写,直接调用,就能用于增加,修改的一些功能,比如提示新增成功,修改成功等,还有按钮组件,弹出框组件等一些基本的组件,对于我自己写的其他代码后续几乎没有冲突,且灵活度高的,我就直接用了框架内的组件,但是一些可控性不强,且不够灵活,几乎不能改动的组件我就会选择自己写,比如树形控件,这个树形控件的样式特别单一,而且不可控,而且灵活度不高,我不是很喜欢,就利用vue的递归组件自己写树形控件,感兴趣的小伙伴可以看下我下篇博客,马上就发《vue如何用递归组件写树形控件》,总结一句话,灵活度高,就用框架,灵活度不高,就自己写,自己写的可控性很强!
关于封装组件
有时候项目中经常会碰到这种情况,好几个页面的列表页除了表头不一样,几乎样式是一模一样的,这样的你就可以封装一个列表组件了,但是只适用于样式差不多的列表,如果你觉得都封装进一个组件里面,对后续开发的一些可控性不是很强,而且每个页面的需求都不一样,处理方式和方法都不很一样,那么我劝你还是老老实实自己写列表吧,因为你封装到一个组件中,对于每个页面的处理方式都要进行判断,这样会显得这个组件很臃肿,我觉得还不如分开写,所以是否要把一个功能模块封装进组件,灵活把握就可以了
关于一些目录和http请求
关于一些目录,比如导航目录,一般是写死的,当然也有从后台获取的,我现在这个项目就是从后台获取目录,还有请求URL,和一些http请求,你就可以新建一个server目录,然后分模块的写道js文件夹里面,return出去,然后再在其他页面中引入,这样做的好处是,模块化管理,将常量和http请求进行统一管理,比如下图
我就把http请求进行了统一管理,然后暴露出去,将来有改动我就可以直接改这里就可以了
关于vuex
下面说一说vuex,也许有许多小伙伴对于这个都不知道干嘛用的,根据我的理解,vuex并不是那么神秘,就是一个公共状态库,是互不联系的两个组件的通信桥梁,比如我又一个列表组件1,又有一个列表组件2。这两个组件完全不一样,丝毫没有联系的那种,但是它们之间需要通信,比如都需要根据一个布尔值show来进行显示还是隐藏,这样就可以把这个show放进vuex状态库里面,当这个show改变的时候,这两个列表组件就可以根据show的变化自动的进行显示隐藏了
关于http请求写在哪里的问题
我一开始接触vuex的时候,vuex里面有一个actions,既可以进行同步操作,又可以进行异步操作,官网上是推荐将http异步请求放在actions里面,因为当时刚学,没有判断能力,就按照官网上来写,在vue文件里面只写逻辑代码,而把所有的http异步请求全部写在了actions里面,这样问题就来了,首先mutations里面的函数必须要用它来提交,而且vue里面通过引入import { mapGetters , mapActions} from ‘vuex‘,来获取到函数和状态,我发现写一个http需要写好多代码,我如果直接写道methods里面,就特别简单,后来我就在想到底放哪里简单些,综合了网上的一些看法,我觉得这跟个人代码风格有关系,官网是推荐写到actions里面,可是如果一个页面里面的一些http请求只是自己用,也不跟其他页面以及组件进行通信的话,我觉得没有必要写到vuex里面,写在methods里面反而简单些,反正到最后项目的数据都能流通就可以了,我觉得一些http请求得到的数据要和其他组件或页面共用的话,我觉得必须要放在vuex里面,这是最棒的办法,如果是自己在自己的小窝里小打小闹,也不影响别人,那么我们还是写在methods里面吧,毕竟别人又不用,为什么要把它驱逐家门呢,哈哈!!
就说到这里吧
Vue项目经验
动态添加class或style
有时候希望根据某个状态值来决定是否添加某个类,可以这样:
// 数组语法
<div :class="[‘a‘,value==true?‘b‘:‘‘]"> //类a总是存在,当value为真时类b存在
// 对象语法
<div :class="{‘a‘:true,‘b‘:value}"> //类a,b都由value控制
<div :style="{background:‘url(‘+value.url+‘)‘}"> //对于style,最好用对象语法,属性值取自当前vue实例
<div className={style.root} style={{ background: `url(${graduateCelebrate}) center center/ 100% 100% no-repeat content-box` }}>
绑定事件函数
有两种方式:
// 方式1
<div @click=‘handle‘></div> //handle函数的this指向当前vue实例,并且会将event对象传入handle作第一个参数,通常可用event.target获取被点击的DOM元素
// 方式2
<div @click=‘handle("param1",$event)‘></div> //handle函数的this指向当前实例,也可将$event传入handle,作用同上,一般用来获取被点击的DOM元素
关于箭头函数
vue的一些配置项,如mounted,beforeRouteEnter,beforeRouteLeave等,本身是一个函数,若使用箭头函数语法,则其内的this对象不是当前vue实例。因此这三个配置项不要用箭头函数,其它的配置项,如methods,computed,watch等接受对象作为配置的,其内的函数最好用箭头函数,另外setTimeout(fn,time)中的fn最好也用箭头函数。
watch对象变化:
‘messages‘: { // messages是一个数组,监听数组或对象的变化
handler: function(newValue, old) {
if (newValue.length == 0) {
setTimeout(() => {
history.back();
}, 1000)
}
},
deep: true
}
关于beforeRouteEnter和beforeRouteLeave
beforeRouteEnter(to,from,next) 无法访问当前vue实例,一般用于从localStorage中读取数据恢复页面,比如从页面a到b,在从b回到a,若a有数据变化(ajax拉取)就更新页面,否则不更新;或者还原当前滚动条位置
beforeRouteLeave(to,from,next) 可访问当前vue实例,一般用于页面离开时将某些状态保存在localStorage中,例如当前滚动条位置
二者都必须显示调用next函数
keep-alive和router-view混合使用
// 这是目前用的比较多的方式
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>
...
routes: [
{ path: ‘/‘, redirect: ‘/index‘, component: Index, meta: { keepAlive: true }},
{
path: ‘/common‘,
component: TestParent,
children: [
{ path: ‘/test2‘, component: Test2, meta: { keepAlive: true } }
]
}
....
// 表示index和test2都使用keep-alive
若有三个页面A->B->C,希望A->B时B拉新数据;C返回B时B不拉新数据,且保存B到C之前的位置,该如何做?
1、给B设置keepAlive:true;
2、在B的beforeRouteEnter中做获取数据的操作,该钩子的三个参数可以判断B的上一个页面,从而决定是否拉数据;
在B的beforeRouteLeave里用localStorage记录位置,并清除和页面展现相关的数据状态;
3、在接口回调里最好用EventCenter这种观察者模式来触发数据获取成功的事件
4、在B的mounted钩子里接受数据获取成功的事件,就可以拿到数据
5、在B的activated钩子里取localStorage里的位置
router-link常用方式
<!-- 命名的路由 -->
<router-link :to="{ name: ‘user‘, params: { userId: 123 }}">User</router-link>
<!-- 带查询参数,下面的结果为 /register?plan=private -->
<router-link :to="{ path: ‘register‘, query: { plan: ‘private‘ }}">Register</router-link>
//对应js
this.$route.params.userId
this.$route.query.plan
过度动画
单元素过渡
transition包一个含有v-if或v-show指令的元素就行
动画过程
多元素过渡
transition下只能有一个根元素,所以多元素时必须用v-if,v-else来保证只有一个,此外,多个相同标签时,最好给每个标签设置key
多组件过渡
用动态组件即可
列表过渡
需用transition-group组件包裹,被包裹元素必须设置key,其他和单元素一样
数据拉取
数据获取操作最早可以放在每个组件的beforeRouteEnter里,然后利用观察者模式把数据传到beforeMount钩子函数里做逻辑操作
beforeRouteEnter(to, from, next) {
let feed_id = to.params.id;
if (from.name != ‘CommentDetail‘ && feed_id) {
getArticleDetail({
feed_id: feed_id
}).then(res => EventCenter.$emit(‘renderArticle‘, res));
getComment({
feed_id: feed_id
}).then(res => EventCenter.$emit(‘renderComment‘, res));
}
next();
}
beforeMount(){
// 1、渲染文章详情
EventCenter.$on(‘renderArticle‘, res => {
});
// 2、渲染文章评论
EventCenter.$on(‘renderComment‘, res => {
})
}
插件 or 组件
对于全局使用频率较高的最好写成插件,而不是组件。例如loading,toast之类,若写成组件,则需要在每个父组件中引入该组件,比较麻烦,写成插件就可以直接调用
import $ from ‘n-zepto‘;
import Vue from ‘vue‘;
export default {
install() {
var timer = null;
Vue.prototype.$alerTip = function(text, delay, options) {
var defaultCssObject = {
position: ‘fixed‘,
display: ‘flex‘,
alignItems: ‘center‘,
justifyContent: ‘center‘,
zIndex: 10000,
minWidth: ‘1.5rem‘,
margin: ‘0 auto‘,
left: ‘calc((100% - 1.5rem) / 2)‘,
bottom: ‘1.4rem‘,
borderRadius: ‘.1rem‘,
height: ‘.5rem‘,
lineHeight: ‘.5rem‘,
background: ‘#000‘,
opacity: 0.85,
color: ‘white‘,
};
var cssObject = options || defaultCssObject;
var temp = `<div class="v-toast">${text}</div>`;
var $div = $(temp);
$div.css(cssObject);
$(‘body‘).append($div);
clearTimeout(timer);
timer = setTimeout(() => {
$(‘.v-toast‘).remove();
timer = null;
}, delay);
}
}
}
vue父子组件生命周期执行顺序问题
父子组件钩子执行顺序:
father created
father before mounted
son created
son before mount
son mounted
father mounted
所以,你在父组件 beforeMounted 中已初始化的值一定可以通过props下发到子组件
vue-router切换页面滚动条位置问题
注意:scrollBehavior只有在history.pushState可用时才有效,而该方法仅兼容到IE10(移动端可放心使用)
const router = new VueRouter({
routes,
scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
if (from.meta.keepAlive) { // 通过meta属性精细控制
from.meta.savedPosition = document.body.scrollTop;
}
return { x: 0, y: to.meta.savedPosition || 0 }
}
}
})
vue库的引入问题
vue提供众多版本库文件,在使用*.vue单文件组件的开发模式时,只需引入vue.runtime.min.js即可,该库体积最小,不过要注意初始化vue实例时,不能用template选项,而应该换为render
new Vue({
el: ‘#app‘,
router,
render: h => h(App)
})
// webpack对应修改:
alias: {
‘vue$‘: ‘vue/dist/vue.runtime.min.js‘
}
vuex组件中访问state报错
TypeError: Cannot read property ‘state’ of undefined”
在组件中使用this.$store.state.test访问state的属性报错,是因为store的实例并未注入到所有的子组件,需修改main.js
new Vue({
el: ‘#app‘,
store, //将store注入到子组件
router,
components: { App },
template: ‘<App/>‘
})
原文:https://www.cnblogs.com/leftJS/p/10628126.html