前言
如果不足之处还望各位大佬留言提出指正,我加以补充修改。谢谢各位啦!
VUE最快入门途径就是官网:cn.vuejs.org/v2/guide/
本文是window环境下除了安装与配置不同,vue知识是不变的。
1.安装与配置
install安装与uninstall卸载
node下载
node -v 查看node版本(检查是否安装)
1.node官网下载:nodejs.cn/download/
2.菜鸟教程下载介绍:www.runoob.com/nodejs/node…
npm下载(若安装了cnpm则下面npm都可以换成cnpm)
npm -v 查看npm版本
安装方式:
1.新版的node已集成了npm,所以之前npm也一并安装好了。
2.安装:npm install npm -g (-g代表全局安装,没有则局部安装)
3.使用淘宝镜像命令:npm install -g cnpm --regisy=registry.npm.aobao.org (安装cnpm比npm更快安装插件)
npm install + 插件(模块名)安装 (若模块后跟@加数字是为此模块的版本号,有时候模块的版本也是个坑,比如sass版本不能太高)
npm uninstall + 插件(模块名)卸载
npm list -g 查看所有全局安装的模块
npm list + 模块 查看某个模块的版本号(可以检查此模块是否安装)
// install 可以简写成 i (按装)有些要注意只能install
// --global 可以简写成 -g (全局)
// --save 可以简写成 -S (保存)
// -y 表示(yes)不用输入回车 比如:npm init -y 省去了敲回车的步骤
vue-cli(脚手架)下载
npm install vue-cli -g (全局安装vue-cli)
webpack下载(一般vue-cli自带不用特定去安装)
webpack -v(查看是否安装)
npm install webpack -g
vue-cl2和vue-cli3环境共存
2.vue项目安装部署(vue-cli)
一、小demo或刚上手练习情况下不用vue-cli
类似jq引用cdn上js文件:
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<body>
<div id="app">
<p>{{ message }}</p>
</div>
<script>
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!'
}
})
</script>
</body>
二、vue-cli下(版本为2)
下面一般回车(y表示yes即默认相当于回车)表示默认安装,不回车自己输入或选择,n代表no表示不安装
vue init webpack 项目名(名字最好不要用驼峰式(你可以试试用驼峰),多个单词可用-来连接,例如:my-project)
? Project name my-project // 项目名,回车
? Project description A Vue.js project // 项目描述
? Author lxf // 作者
? Vue build standalone // 构建,回车
1.Runtime + Compiler(运行加编译)
2.Runtime-only(只运行)
? Install vue-router(y/n)? 回车 // 是否安装路由
? Use ESLint to lint your code(y/n)? n // 代码格式标准就连标点符号位置错了都会报错的检验,这里对于初学者来说不安装填个n,不安装
? Setup unit tests? n // (是否安装单元测试,看情况)
? Setup e2e tests with Nightwatch? n // 是否安装e2e测试,看情况一般不装
? Should we run `npm install` for you after the project has been created?(recommended)// 是否安装使用npm装,回车(看环境,window是npm)
1.Yes. use NPM
2.Yes. use Yarn
3.No. I will handle that myself
vue-cli · Generated "my-project".
To get started: // 看到这部分就代表安装完成
cd my-project // 切换到你项目名的目录
npm install // 安装node_module模块
npm run dev // 运行
3.主要文件介绍
在vue init webpack 项目名新建vue项目时webpack已经配置好不需要太大的改变。具体的webpack配置可以去官网学习。
1.build/webpack.base.conf.js
const path = require('path')
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
module.exports = {
resolve:{
alias:{
'@': resolve('src') // 后面引用@当做src的路径
}
}
}
2.config/index.js
1.跨域问题
dev:{
proxyTable:{ // 这就是vue解决跨域的方式
"/apis":{ // axios配置defaults.baseURL,默认地址
target: "http://www.lxf.com", // 你的目标接口域名
changeOrigin: true, // 是否跨域
pathRewrite:{ // 重写接口
'^/api': ''
}
}
}
}
在配置axios的文件下:
axios.defaults.baseURL = '/apis' // 记得一一对应
axios.defaults.baseURL = {
development: "开发环境地址",
test: "测试环境地址",
production: "上线发布环境地址"
}[process.env.NODE_ENV] // process.env.NODE_ENV是环境
使用时this.$axios.post('/login', data).then(res => {})
http://www.lxf.com/login
// 自动帮你补齐了前面路径,不能再写完整的了
如下图所示问题:
在axios中设置(或者不要withCredentials属性):
withCredentials: false
2.文件说明
sesetsSubDirectory: 'static', // 编译输出的二级目录
assetsPublicPath: './' // 编译发布的根目录,可配置为资源服务器域名或者cdn域名
host: 'localhost', // 开发时的访问域名
port: 8080, // 默认端口
autoOpenBrowser: true, // 是否自动打开浏览器(打开http://localhost:8080)
errorOverlay: true, // 在浏览器中是否展示错误蒙层(报错黑白照一样)
notifyOnErrors: true, // 是否展示错误的通知
poll: false, // 指定webpack检查文件的方式
useEslint: false, // 检查代码格式,安装时也有出现,标准规范(符号位置都不能错的)建议false
showEslintErrorsInOverlay: false, // 在浏览器上,eslint错误是否以蒙层的方式展示
devtool: 'eval-source-map', // source maps的格式
cacheBusting:true, // 是否通过在文件名称后添加一个查询字符串来创建source map的缓存
cssSourceMap: false, // 是否记录你压缩的翻译文件,通过这个文件可以找到你的对应的源码
index: path.resolve(__dirmame, '../dist/index.html'), // html文件生成的地方
assetsRoot: path.resolve(__dirname, '../dist'), // 编译生成的文件的目录
assetsSubDirectory: 'static', // 编译生成的静态文件的目录
assetsPublicPath: '/', // 编译发布的根目录,可配置为资源服务器域名或者cdn域名
productionSourceMap: false, // 打包的文件中会出现一些map文件,map文件的作用在于:项目打包后,代码都是经过压缩加密的,如果运行时报错,输出的错误信息无法准确得知是哪里的代码报错,true使得生产环境里可以看到所有的源码,所有要关掉
devtool: '#source-map', // vue的调试插件
productionGzip: false, // 是否开启生产环境的gzip压缩
productionGzipExtensions: ['js', 'css'], // 开启gzip压缩的文件的后缀名称
bundleAnalyzerReport: process.env.npm_config_report, // 如果这个选项是true的话,那么则会在build后,会在浏览器中生成一份bundler报告
主要打包优化:
productionSourceMap: false, // 减少代码包
productionGzip: true, // 对项目代码中的JS/CSS/SVG(*.ico)文件进行gzip压缩
参考文献:
3.打包后图片和js路径不对
npm run build 进行打包
config文件下的index.js
build:{
assetsPublicPath: '/' ==> 改成assetsPublicPath: './'
}
build文件夹下的utils文件
if (options.extract) {
return ExtractTextPlugin.extract({
use: loaders,
fallback: 'vue-style-loader',
publicPath: '../../' // 处理打包后找不到静态文件的问题
})
} else {
return ['vue-style-loader'].concat(loaders)
}
4.git上传dist目录
vue-cli默认不上传打包后的dist目录
找到.gitignore文件(在index.html上面点)
删掉/dist/ 这一行
这里就是声明git不上传一些文件的
5.main.js文件
Vue.config.productionTip = false // 阻止启动(npm run dev)生产消息
4.配置项目建好后安装一些固定的库
1.安装axios
npm install axios --save
在main.js配置内容:
import axios from 'axios'
Vue.prototype.$axios = axios
this.$axios.post(地址, 参数).then(res => {})
1.1 post请求方法(data)
this.$axios.post('地址',
data: {
参数1: 值1,
参数2: 值2
}
}).then(res => {
console.log(res) // 成功
}).catch(err => {
console.log(err) // 错误
})
1.2 get请求方法(params)
this.$axios.get('地址', {
params: {
参数1: 值1,
参数1: 值2
}
}).then(res => {
console.log(res) // 成功
}).catch(err => {
console.log(err) // 错误
})
1.3 delete请求方法
1.请求参数拼接在url上(params)
this.$axios.delete('地址', {
params: {
参数1: 值1,
参数1: 值2
}
}).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
2.请求参数放在请求体(data)
this.$axios.delete('地址', {
data: {
参数1: 值1,
参数1: 值2
}
}).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
3.请求参数拼接在url上或请求参数放在请求体
this.$axios({
method: 'delete',
url: '/delete',
params: {}, // 请求参数拼接在url上
data: {} // 请求参数放在请求体
}).then(res => {
console.log(res)
})
2.安装qs
配置qs解码(post传参数的坑)
npm install qs // 安装
import qs from 'qs' // main.js里配置
Vue.prototype.$qs = qs // $qs前面的$只是为了区别作用(this.$qs来取,Vue.prototype原型直接用this取) // 若有封装,直接在封装那边使用
使用时:
$qs.stringify(params) // 将对象序列化成url的形式;以&进行拼接
$qs.parse(params) // 将url解析成对象形式
举例(若axios有配置就不必再写$qs.stringify()):
this.$axios.post({url, data: this.$qs.stringify(data)}).then(res => {}).catch(err => {})
在axios设置时候使用:
axios.js:
import qs from 'qs'
let instance = axios.create({
transformRequest: [function (data) { // 设置请求的参数格式,用qs转换下
return qs.stringify(data)
}]
})
3.axios大概配置(这只是参考demo大概方向)
import axios from 'axios'
import qs from 'qs'
if (process.env.NODE_ENV == 'development') {
axios.defaults.baseURL = '开发地址'
} else if (process.env.NODE_ENV == 'test') {
axios.defaults.baseURL = '测试地址'
} else if (process.env.NODE_ENV == 'production') {
axios.defaults.baseURL = '生产地址'
}
上面优化:
axios.defaults.baseURL = {
development: "开发地址",
test: "测试地址",
production: "生产地址"
}[process.env.NODE_ENV]
axios.defaults.baseURL = '/apis' // 对应config/index.js中proxyTable代理
let instance = axios.create({
hearders: {
get: {
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
},
post: {
'Content-Type': 'application/json;charset=UTF-8'
}
},
timeout: 10000,
data: {},
responseType: 'json',
withCredentials: true, // 当前请求为跨域类型时是否在请求中协带cookie
transformRequest: [function (data) {
return qs.stringify(data)
}]
})
// 添加请求拦截器
axios.interceptors.request.use(config => {
return config
}, error => {
return Promise.reject(error)
})
// 添加响应拦截器
axios.interceptors.response.use(response => {
if (response.status === 200) {
return Promise.resolve(response)
} else {
return Promise.reject(response)
}
// return response
}, err => {
if (err && err.response && err.response.status) {
let msg = err.message // 错误信息
switch (err.response.status) {
case 400:
msg = '请求错误'
break
case 401:
msg = '未授权,请登录'
break
case 403:
msg = '拒绝访问'
break
case 404:
msg = `请求地址出错: ${err.response.config.url}`
break
case 408:
msg = '请求超时'
break
case 500:
msg = '服务器内部错误'
break
case 501:
msg = '服务未实现'
break
case 502:
msg = '网关错误'
break
case 503:
msg = '服务不可用'
break
case 504:
msg = '网关超时'
break
case 505:
msg = 'HTTP版本不受支持'
break
default:
}
console.log(msg)
}
return Promise.reject(err)
})
export default instance
/**
* post 请求方法
* @param url
* @param data
* @returns {Promise}
*/
export function post (url, data = {}) {
return new Promise((resolve, reject) => {
instance.post(url, qs.stringify(data)).then(res => {
// 对接口错误码做处理
resolve(res.data)
}, err => {
reject(err.data)
})
})
}
/**
* get 请求方法
* @param url
* @param data
* @returns {Promise}
*/
export function get (url, data = {}) {
return new Promise((resolve, reject) => {
instance.get(url, {
params: data
}).then(res => {
resolve(res.data)
}).catch(err => {
reject(err.data)
})
})
}
get参数转码
在添加请求拦截器request中转码
axios.interceptors.request.use(config => {
let url = config.url
if (config.method === 'get' && config.params) { // get参数编码
url += '?'
let keys = Object.keys(config.params)
for (let key of keys) {
url += `${key}=${encodeURIComponent(config.params[key])}&` // encodeURIComponent()转码
}
url = url.substring(0, url.length - 1)
config.params = {}
}
config.url = url
return config
}, error => {
console.log(error)
return Promise.reject(error)
})
# 编码解码url参数
## 编码用 ## 解码用
escape(str) unescape(str) // 除了ASCII 字母、数字和特定的符号外,对传进来的字符串全部进行转义编码
encodeURI(str) decodeURI(str) // 用于编码整个URI,因为URI中的合法字符都不会被编码转换。
encodeURIComponent(str) decodeURIComponent(str) // 讲参数中的中文、特殊字符进行转义,而不会影响整个URL。
4.vue中使用scss
npm install node-sass --save-dev // 安装node-sass
npm install sass-loader@7.3.1 --save-dev // 安装sass-loader(特定安装版本,免得太高报错)
// 安装style-loader 有些人安装的是 vue-style-loader 其实是一样的,有就不用安装
// npm install style-loader --save-dev
build/webpack.base.config.js:(module的rules数组下再添加配置scss)
module: {
rules: [
{ // 配置scss
test: /\.scss$/,
loaders: ["style", "css", "sass"]
}
]
}
使用时:
<style scoped lang="scss"></style>
若安装中出现Modele build failed: TypeError: this.getResolve is not a function at Object.loader...错误
是因为安装的sass版本过高,换个低一点版本。
卸载当前版本 npm uninstall sass-loader
安装 npm install sass-loader@7.3.1 --save-dev
5.vue移动端使用rem
假如设计稿的宽度是750px,此时1rem应该等于75px。
npm install lib-flexible --save 安装淘宝适配rem
npm install px2rem-loader --save-dev // 使用px2rem-loader自动将css中的px转换成rem
build/utils.js(exports.cssLoaders)下添加:
const px2remLoader = {
loader: 'px2rem-loader',
options: {
remUint: 75 // ui设计图750尺寸
}
}
// 注释掉这一行
// const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]
// 修改为
const loaders = [cssLoader, px2remLoader]
if (options.usePostCSS) {
loaders.push(postcssLoader)
}
// 要适用平板上,那就再添加个
<script>
/(iPhone|iPad|iPhone OS|Phone|iPod|iOS)/i.test(navigator.userAgent)&&(head=document.getElementsByTagName('head'),viewport=document.createElement('meta'),viewport.name='viewport',viewport.content='target-densitydpi=device-dpi, width=480px, user-scalable=no',head.length>0&&head[head.length-1].appendChild(viewport))
</script>
最后重新 npm run dev (每更改下webpack配置都要重新运行下,免得配置了没用)
后面直接px上单位,自动帮你转化成rem。750px就写750px!
6.移动端300ms延迟
cnpm install fastclick -S // 安装fastclick
main.js:
import FastClick from 'fastclick' // 引入插件
FastClick.attach(document.body) // 使用 fastclick
5.vue目录结构
build 项目构建(webpack)相关代码
config 配置目录,包括端口号等。
node_modules npm 加载的项目依赖模块
src
这里是我们要开发的目录,基本上要做的事情都在这个目录里。里面包含了几个目录及文件:
assets: 放置一些图片,如logo等。
components: 目录里面放了一个组件文件,可以不用,自己建个文件夹存放(组件放一起,页面放一起等进行分类)。
App.vue: 项目入口文件,我们也可以直接将组件写这里,而不使用 components 目录。
main.js: 项目的核心文件。
static 静态资源目录,如图片、字体等。
test 初始测试目录,可删除
.xxxx文件 这些是一些配置文件,包括语法配置,git配置等。
index.html 首页入口文件,你可以添加一些 meta 信息或统计代码啥的。
package.json 项目配置文件。
README.md 项目的说明文档,markdown 格式
6.vue页面介绍(懂this很重要)
一个完整的.vue文件需要三部:template,script,style
1.页面结构
<template>
<div class="hello">{{msg}}</div> // template里面最外层一般都要一个div或者(别的)包围里面多个div或者别的标签
</template>
例如:
<template>
<div></div>
<p></p>
</template> // 这是错的,这两个标签外再用一个标签包起来
<script>
export default {
name: 'hello', // 绑定template的最外层class,可不要
data () {
return {
msg: 'Welcome to Your Vue.js App lxf'
},
created () {}, // 在实例被创建之后被调用,在模板渲染成html前调用,即通常初始化某些属性值,然后再渲染成视图
mounted () {}, // 在模板渲染成html后调用,通常是初始化页面完成后,再对html的dom节点进行一些需要的操作
watch: {}, // 监测Vue实例上的数据变动
computed: {}, // 计算属性
methods () {}, // 放方法
filters: {}, // 放过滤器
}
}
</script>
<style scoped></style>
// style中的scoped相当于js中一个作用域,意思是这里的style只属于此页面的style(局部样式),若没有scoped相当于全局样式
2.this
全局环境中,this在浏览器指window对象,this在node下指global对象。
一般情况下,谁调用this,this指向谁。
1.普通函数调用
function fn(){
console.log(this === window) // 注意环境,node环境下global,非node环境下window
}
fn() // true,在全局环境下运行this就是指window
2.当一个函数被当作一个对象的方法调用的时候
let a = 'jkl'
let obj = {
a: 'lxf',
b: function (){
console.log(this) // { a: 'lxf', b: [Function: b] } // obj调用b函数所以this,指向当前对象(obj)
console.log(this.a) // lxf
}
}
obj.b() // this指当前的这个对象
let x = obj.b // x直接指向了一个函数的引用,这时候,和obj这个对象没有关系了
x() // this指window对象,因为全局调用x函数(变成了一般函数调用),所以this.a是jkl
//当然也可以写成这样
function test() {
console.log(this.a)
}
let obj = {
a: 'lxf',
b: test
}
obj.b() // lxf
3.在构造函数(new Fun())中,表示实例化出来的对象
function Fn(){
this.name = 'lxf'
console.log(this) // Fn { name: 'lxf'}
console.log(this.name) // lxf
}
let a = new Fn()
console.log(a) // Fn { name: 'lxf'}
4.call,apply绑定this(指定执行函数中 this 的上下文)
call,apply都是为了改变函数体内部 this 的指向。call和apply区别是带的参数格式不同。
fn1.call(this, arg1, arg2...) // 参数列表arg1,arg2
fn2.apply(this, [arg1, arg2...]) // 参数数组 [arg1,arg2]
fn1.call(this)
gn2.apply(this)
// 例如:
let obj1 = {
name: 'lxf'
}
let obj2 = {
name: 'jkl'
}
function foo() {
console.log(this)
}
foo.call(obj1) // 绑定obj1,所以this指obj1
foo.call(obj2) // 绑定obj2,所以this指obj2
function Person(name){
this.name = name
console.log(this)
}
let obj = {
name: 'lxf'
}
let Person2 = Person.bind(obj)
let p = new Person2('jkl')
// Person {name: "jkl"}
console.log(obj)
// Object {name: "lxf"}
5.箭头函数的this
箭头函数没有自己的this,是继承自父执行上下文的this。它的this默认指它所在环境或对象。 执行定义它的作用域中的 this,与如何调用以及在哪里调用它无关,包括 (call, apply, bind) 等操作都无法改变它的 this指向。
在vue中this指的就是new Vue()出来的实例。
let obj = {
name: 'lxf',
fun: () => {
console.log(this) // window
}
}
obj.fun()
6.异步回调函数(setTimeout, setInterval等)
let obj = {
name: 'lxf',
foo: function() {
console.log(this) // obj
},
foo2: function() {
console.log(this) // window
setTimeout(this.foo, 1000) // setTimeout是个函数,this.foo相当于参数。在传入参数的时候,把`this.foo`看成一个整体`fun`,相当于`fun = this.foo`。 `fun` 指向 `this.foo` 的引用,执行的时候其实是执行了 `fun()` 所以已经和 `obj` 无关了,它是被当作普通函数调用的,即this指window
}
}
obj.foo2()
// 解决方案(闭包或者箭头函数)
let obj = {
name: 'lxf',
foo: function() {
console.log(this)
},
foo2: function() {
console.log(this) // obj
// 使用闭包
let _this = this
setTimeout(function(){
console.log(_this.name) // lxf
console.log(this) // window
}, 1000)
// 使用箭头函数
setTimeout(() => {
console.log(this.name) // lxf
}, 1000)
}
}
obj.foo2()
7.vue的生命周期
vue的生命周期
Vue 实例在被创建时都要经过一系列的初始化过程。
从开始创建、初始化数据、编译模版、挂载Dom -> 渲染、更新 -> 渲染、卸载等一系列过程,我们称这是Vue的生命周期。
同时在这个过程中也会运行一些叫做生命周期钩子的函数。
通俗来讲:我们从受精卵(new Vue())(beforeCreate)胚胎(created),(beforeMount)婴儿(mounted),(beforeUpdate)少儿,青年,成年,老人(updated),最后到(beforeDestroy)死亡(destroyed)的过程。
而我们在这个过程中每个时期我们能做什么和不能做什么。
具体效果可以创建一个例子看看。
beforeCreate() { // 组件实例被创建之初,组件的属性生效之前
console.log('调用了beforeCreate')
console.log(this.message)
console.log(this.$el)
},
created() { // 组件实例已经完全创建,属性也绑定,但真实dom还没有生成,$el还不可用
console.log('调用了created')
},
beforeMount() {
console.log('调用了beforeMount')
}
等等之类,看看到底发生了什么(主要的会用就行,不过面试官挺喜欢问问这个题目)
vue的生命周期图
8.指令篇
1.基础指令(v-)
v-html
v-html,v-text此处为单向绑定,数据对象上的值改变,插值会发生变化;但是当插值发生变化并不会影响数据对象的值。 输出真正的 HTML。
v-text
输出元素的文本内容,相当于{}。
v-model
input、select、textarea、checkbox、radio 等表单控件元素上创建双向数据绑定,根据表单上的值,自动更新绑定的元素的值。
v-once
只显示一次
v-bind
通常用来绑定样式(class,id等),一般根据true或者false
v-on
事件触发
<div id="app">
<p>{{ message }}</p>
<p v-model=”message”></p>
<p v-html=”html”></p>
<p v-text=”html”></p>
<span v-once>这个将不会随msg属性的改变而改变: {{ message }}</span>
</div>
data: { // 在data里定义值
return {
message: '文本',
html: '<h1>html显示</h1>'
}
}
<div id="app">
<p v-bind:title=”message”>{{ message }}</p>
<p>{{ 1 + 1 }}</p>
<p v-bind:class=”'list-' + className”></p>
<p v-if=”seen”>seen是true显示我</p>
<p v-else>seen是false则显示我</p>
<p v-show=”seen”>seen是true显示我</p>
</div>
data: { // 在data里定义值
return {
message: '文本',
seen: true,
className: 'text'
}
}
v-bind: 简写成: 例如:<p v-bind:href=''></p> 缩写成 <p :href=''></p>
v-on:简写成@ 例如:<p v-on:click=''></p> 缩写成 <p @click=''></p>
在{{}},v-bind等中可进行js表达式。
HTML 属性中的值应使用 v-bind
指令(常用于class,img等标签的属性值绑定)。
v-for
<ul>
<li v-for="(item, index) in arrs" :key="item.id">
我叫{{ item.name }},今年{{ item.age }} 岁
</li>
</ul>
data () {
return {
arrs: [
{id: '1', name: 'j', age: 12},
{id: '2', name: 'k', age: 18},
{id: '3', name: 'p', age: 13}
]
}
}
item:数组的每一项,index:数组的索引值(可不要)
:key:绑定的是数组的唯一值,当没有唯一指代时可用索引代表(最好要有)
2.v-if和v-show区别:
v-if是通过控制dom节点的存在与否来控制元素的显隐;
v-show是通过设置DOM元素的display样式,block为显示,none为隐藏 。
如果需要非常频繁地切换,则使用 v-show 较好;
如果在运行时条件很少改变,则使用 v-if 较好。
v-if判断是否加载,可以减轻服务器的压力,在需要时加载,但有更高的切换开销;
v-show调整DOM元素的CSS的dispaly属性,可以使客户端操作更加流畅,但有更高的初始渲染开销。
3.修饰符
指出一个指令应该以特殊方式绑定,语法就是一个指令后加个点和修饰符
1.表单修饰符:
1.1(.lazy)
v-model 在每次 input 事件触发后将输入框的值与数据进行同步 。你可以添加 lazy 修饰符,从而转变为使用 change 事件进行同步。
<input v-model.lazy="msg" ><!-- 在“change”时而非“input”时更新 -->
1.2(.trim)
自动过滤首尾空白字符
1.3(.number)
输入字符串转为有效的数字(字符串变成数值类型),只是类型变化,保证你输入的数字是数字类型不是字符串类型。
2.事件修饰符:
2.1(.stop)
由于事件冒泡的机制,我们给元素绑定点击事件的时候,也会触发父级的点击事件。相当于调用了event.stopPropagation()方法。
2.2(.prevent)
用于阻止事件的默认行为,例如,当点击提交按钮时阻止对表单的提交。相当于调用了event.preventDefault()方法。
2.3(.self)
只当事件是从事件绑定的元素本身触发时才触发回调,例如可以解决事件冒泡。
2.3(.once)
只能用一次,绑定了事件以后只能触发一次,第二次就不会触发。
2.4(.capture)
默认的事件触发是从目标开始往上冒泡,而capture事件触发从包含这个元素的顶层开始往下触发(相反)。
2.5(.passive)
监听元素滚动事件的时候,会一直触发onscroll事件,在移动端中会让我们的网页变卡。
<div @scroll.passive='onScroll'></div> // 阻止一直触发
2.6(.native)
在组件中事件使用,因为组件上的事件没用,native把一个vue组件转化为一个普通的HTML标签
<my-componet @click.native=''></my-componet>
例如:
引用element库的某组件时,@keyup.enter不能用
<el-input v-model="name" placeholder="账号" @keyup.enter="login"></el-input>
解决:
<el-input v-model="name" placeholder="账号" @keyup.enter.native="login"></el-input>
注意:使用.native修饰符来操作普通HTML标签是会令事件失效的
3.鼠标按钮修饰符:
3.1(.left)左键点击
3.1(.right)右键点击
3.1(.middle)中键点击
4.键盘修饰符:(keyup,keydown。。。)
4.1(.keyCode)(百度键码对应表)常见键名有enter,space,delete,esc等
<input type="text" @keyup.enter=''>
<input type="text" @keyup.13=''> // 13就是enter的键码值
<input type="text" @keyup.ctrl.13=''> // ctrl+enter一起按触发
4.2(.exact)限制系统修饰键(防止你按下特定的加别的也有效,比如你想要的是ctrl+c,但是ctrl+shift+c也有效)
<input type="text" @keydown.enter.exact="box2">
5.v-bind修饰符:(具体百度)
5.1(.sync)
//父亲组件
<comp :myMessage="bar" @update:myMessage="func"></comp>
func(e){ // js
this.bar = e
}
//子组件js
func2(){
this.$emit('update:myMessage',params)
}
现在这个.sync修饰符就是简化了上面的步骤
//父组件
<comp :myMessage.sync="bar"></comp>
//子组件
this.$emit('update:myMessage',params)
5.2(.prop)
5.3(.camel)渲染为驼峰名
注意:
修饰符可以同时使用多个,但是可能会因为顺序而有所不同。
用 @click.prevent.self 会阻止所有的点击,而 @click.self.prevent 只会阻止对元素自身的点击,也就是从左往右判断。
4.局部自定义指令
在要使用指令的.vue文件(跟data,methods等同级):
data () {
return {}
}
directives: {
color: { // 自定义指令名字
// 钩子函数,被绑定元素插入父节点时调用 (父节点存在即可调用,不必存在于 document 中)。
inserted (el, binding) {
el.focus()
console.log('el')
console.log('inserted')
},
// 只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个在绑定时执行一次的初始化动作。
bind () {
console.log('bind')
},
// 所在组件的 VNode 更新时调用,但是可能发生在其孩子的 VNode 更新之前。
// 指令的值可能发生了改变也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新
updata () {
console.log('updata')
},
// 所在组件的 VNode 及其孩子的 VNode 全部更新时调用。
componentUpdated () {
console.log('componentUpdated')
},
// 只调用一次,指令与元素解绑时调用。
unbind () {
console.log('unbind')
}
}
}
5.全局自定义指令
directive.js
export default (Vue) => {
Vue.directive('color', {
inserted: function (el, binding) {
el.style.color = binding.value
}
})
Vue.directive('font', {
inserted: function (el, binding) {
el.style.fontSize = binding.value + "px"
}
})
}
main.js
import directive from './directive'
Vue.use(directive) // 全局使用directive文件
使用时:
<p v-color="'red'"></p>
9.监听属性(watch)
一般方法
msg (val, oldVal) {
// val是新值,oldVal是旧值
}
对象采用的深度监听
info: {
handler (obj) {
// handler是固定写法,参数obj是监听的对象info
},
deep: true // 深度监听开启
}
监听对象中某一个属性:(浅度监听不需要deep)
'info.msg' (val, oldVal) {
// info.msg指你要监听的对象info中的msg一项,记得引号
}
监听路由
1.方式一
$route (to, from) {
// to现在路由,from过去的路由。路由路劲(path)
}
2.方式二
$route: {
handler (val, oldVal) {
// val新的路由,oldVal旧的路由
},
deep: true
}
3.方式三
watch: {
'$route': 'getPath'
},
methods: {
getPath () {}
}
10.计算属性(computed)
用来处理一些复杂逻辑,要有个返回值return(默认getter里)
1.计算值
<div>{{money}}</div>
computed: {
money () {
// 计算变量,不能在data再声明money,这就是和watch区别之一
}
}
2.计算属性默认只有getter,不过在需要时你也可以提供一个setter(get就是getter,set就是setter固定写法)
fullName: {
get () {
// getter用来读取
},
set () {
// setter(set)用来写入
}
}
3.computed不需要使用this
data: {
return {
num: 12
}
},
computed: {
a () { return 1 },
b ({a, num}) { // 不用引用this
return a + 10 + num
}
}
4.computed和watch监听vuex来请求接口
computed: {
id () {
return this.$store.state.channelArr.length && this.$store.state.channelArr[0].id
}
},
watch: {
id (val) {
if (val) {
this.getAllChannelDetail(val)
}
}
}
11.computed和watch的区别
computed:计算结果并返回,只有当被计算的值发生改变时才会触发(即:计算属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算)。不可在data里定义computed的变量(变量不可被重复定义和赋值)
watch:监听某一个值,当被监听的值发生变化时,执行对应的操作 (与computed的区别是,watch更加适用于监听某一个值的变化并做对应的操作,比如请求后台接口等,而computed适用于计算已有的值并返回结果)
computed特性:
1.是计算值
2.应用:就是简化tempalte里面{{}}计算和处理props或$emit的传值
3.具有缓存性,页面重新渲染值不变化,计算属性会立即返回之前的计算结果,而不必再次执行函数
watch特性:
1.是观察的动作,
2.应用:监听props,$emit或本组件的值执行异步操作
3.无缓存性,页面重新渲染时值不变化也会执行
12.样式绑定(v-bind)
注意细节,字符串用单引号,非字符串用双引号。 防止混乱分不清用哪个
html
<div :class=”{ active: isActive }”>单个</div>
<div :class=”{ active: isActive, 'tx-data': isData }”>多个</div>
<div :class="classObject">对象</div>
<div :class="[class1, class2]">数组</div>
<div :class="[isActive ? class1 : class2]">数组选取(三元)</div>
<div :style="{ color: actColor, fontSize: fontSize + 'px' }">内联样式(少用)</div>
<!--跟前面一样,还可以内联样式对象,数组-->
<div :style="styleObject">一个</div>
<div :style="[baseStyles, styleObject]">多个</div>
script
data () {
return {
isActive: true, // 开启active的样式
isData: true,
classObject: {
isActive: true,
isData: false,
},
class1: 'active',
class2: 'tx-data',
actColor: 'green',
fontSize: 18,
styleObject: {
color: 'red',
fontSize: '13px'
}
}
}
13.组件(component)引用注册使用
import childItem from '@/components/child' // 1.引用组件路径
export default {
components: { // 2.组件注册
childItem, // childItem: 'childItem' es6的简写(采用驼峰式)
},
data () {
return {}
}
}
<child-item>驼峰用-拆开</child-item> // 3.组件使用
14.路由
1.路由设置(router下index.js)
1.方法一
import login from '@/components/login'
{
path: '/login',
name: 'login',
component: login
}
2.方法二(路由懒加载)
{
path: '/test',
name: 'test',
component: resolve => require(['../components/test'], resolve)
}
3.方法三
{
path: '/gridHeatMap',
name: 'gridHeatMap',
component: () => import('@/components/gridHeatMap')
}
大概的demo
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
redirect: '/loginRegister' // 默认直接路径(打开进入的第一个页面路径)
}, {
path: '/loginRegister',
name: 'login',
redirect: '/loginRegister/loginIn', // 默认登录界面
component: resolve => require(['../components/loginRegister/login'], resolve),
children: [ // 子路由
{
path: 'loginIn', // 子路由上不用加“/”
name: 'loginIn',
component: resolve => require(['../components/loginRegister/loginIn'], resolve)
}, {
path: 'register',
name: 'register',
component: resolve => require(['../components/loginRegister/register'], resolve)
}
]
}, {
path: '/interface',
name: 'interface',
component: resolve => require(['../components/interFace'], resolve)
meta: { // 定义元信息
requireAuth: false, // 权限验证标记 false 不进行校验拦截
keepAlive: true // 是否缓存
}
}
]
})
2.路由跳转(设定好路由后)
1.标签跳转
<router-link to='/pathName' tag="li">
跳转到路径为pathName的页面(tag是router-link映射为li标签可不设置)
</router-link>
<router-link :to="{name: 'pathName'}"></router-link>
<router-link :to="{path: '/pathName'}"></router-link>
2.事件跳转
this.$router.push({path: '/pathName'})
this.$router.push('/pathName')
this.$router.go(1) // 返回上一页
this.$router.push({
path: '/pathName',
query: { // query地址栏显示参数有点像ajax的get请求
id: '666' // this.$route.query.id来取参数值
}
})
this.$router.push({
name: ' pathName ',
params: { // params地址栏显示参数有点像ajax的post请求
id:'1' // this.$route.params.id来取参数值
}
})
3.路由拦截(阻止用户直接输入url登录)
router.beforeEach((to, from, next) => {
if (sessionStorage.getItem('userName')) { // 判断是否登录
next()
} else {
if (to.meta.requireAuth) { // router里设置requireAuth是否需要登录权限
// log('需要权限')
next({path: '/login'})
} else {
next()
}
}
})
15.组件之间传值
1.父向子传值v-bind和props
fathe.vue:(父页面)
<child :money1='money2'></child>
import child from child (child.vue的.vue可略)
components: { child },
data () {
return {
money2: '我是爸爸给儿子钱'
}
}
children.vue:(子页面)
<div>老爸给的钱{{ value }}</div>
props: ['money1'], // 子用props接收钱
data () {
return {
value: this.money1 // 接受爸爸给的钱,但不能直接去修改爸爸给的多少钱,钱不够那得申请(子向父传值)(vue中不建议直接操作props会报错),新建个变量进行存放钱
}
}
props说明
props: { // props可数组形式(简单的值)或者对象形式(复杂的值,可验证类型)
msg: {
type: String, // 类型:字符串(类型的首字母都是大写)
required: true, // 这个参数必须传入
default: 100 // 如果不传入的话默认就是100
},
num: Number, // 只接受数值类型的参数
foo: [String, Number], // 可以接受字符串和数值类型的参数
}
props可数组形式(简单的值)或者对象形式(复杂的值,可验证类型)
2.子向父传值v-on和$emit
fathe.vue:(父页面)
<child @money1='fatherMethods'></child>
data () {
return {
money2: '' // 存子传的值
}
},
methods: {
fatherMethods (val) { // val是子传过来的值
this.money2 = val
}
}
children.vue:(子页面)
<button @click='send'>{{ value }}</div>
data () {
return {
value: ''
}
},
methods: {
send () {
this.$emit('money1', this.value)
}
}
3.兄弟之间传值(发送emit和接收on)
1.新建个js文件
bus.js:
import Vue from 'vue'
export default new Vue()
2.组件a传到组件b
a.vue:
import Bus from 'bus' // 引入bus.js所在的路径
methods:{
send () { // 再找个地方调用它(例如放在created自调用)
Bus.$emit('sendA', val) // val为需要传的值
}
}
3.组件b接受组件a的值
b.vue:
import Bus from 'bus' // 引入bus.js所在的路径
methods:{
send () { // 再找个地方调用它
Bus.$on('sendA', val => {
console.log(val) // val为接受到的值
})
} // sendA记得对应组件a定义的事件
}
4.清除监听总线
防止再次传值时多次触发事件
Bus.$off('事件名')
4.attrs和listeners(继承父级属性)
1.常用于编写插件
父组件:
<l-input placeholder="请输入信息"></l-input>
子组件:
<input :placeholder="$attrs.placeholder"> // $attrs继承所有的父组件属性(除了prop传递的属性、class 和 style )
2.有三个组件且关系是这样:A(父级组件)>B(儿子组件)>C(孙子组件),现在把A组件的信息传递给C组件,C组件触发A组件。
父组件:
<test-item @updata="callMe" :placeholder="foo"></test-item>
import testItem from 'testItem'
data () {
return {
foo: '请输入信息'
}
},
methods: {
callMe () {
console.log('我孙子叫我呢')
}
}
子组件:
<child-item v-bind="$attrs" v-on="$listeners"></child-item>
import childItem from 'childItem'
data () {},
inheritAttrs: true
// 默认值true,继承所有的父组件属性(除props的特定绑定)。false不继承,且避免设置到根元素上但class属性会继承。(和data同级)
孙组件:
<button @click="callGrandfather">呼叫爷爷</button>
methods: {
callGrandfather () {
console.log(this.$attrs) // {placeholder: '请输入信息'} (属性中介v-bind="$attrs")
this.$emit('updata') // 触发updata绑定callMe事件(事件中介v-on="$listeners")
}
}
5.可跨级组件通信(provide/inject)
父组件:
data () {},
provide: { // 和data同级
name: "lxf"
}
孙组件:
data () {},
inject: ['name'] // 和data同级相当于props(也是用this.name获取)
6.获取父组件内容$parent
父组件:
data () {
return {
name: 'lxf'
}
}
子组件:
this.$parent.name // lxf
7.获取父组件多个内容slot(插槽)
注意slot位置
父组件:
<child>
<p>匿名插槽</p>
<p slot="lxf">有名插槽</p>
</child>
子组件:
<slot name="lxf"></slot>
<slot></slot>
最后渲染成:
有名插槽
匿名插槽
8.父组件获取子组件的值 $ref
<Children ref="chi"></Children>
用this.$refs.chi来获取子组件data和methods
16.过滤器
过滤器就是对某个名词进行加工,修饰,常用于后端给的值进行处理,比如时间戳,加单位等 (注意:要有个返回值return)
1.局部过滤器
<div id='app'>
<p>{{ toFilter | filterName }}</p> // toFilter是要过滤的(作为第一个参数),filterName过滤器方法名,中间还有个竖杆。
<p>{{ toFilter | filterA | filterB }}</p> // 可以多层过滤
<p>{{ toFilter | filterA('arg1', arg2) }}</p> // toFilter作为第一个参数,arg1第二个。。。。(可多个参数)
<div :id="toFilter | filterName"></div> // 指令上的过滤
</div>
filters: {
filterName (参数1,参数2。。。) {}
}
2.全局过滤器
main.js:
Vue.filter('filterName', function (value) {})
3.举例子
<div id="app">
<h1>{{ price | toFixed(2) }}</h1>
</div>
filters: {
toFixed (val, num) { // val是参数price,num是自定义参数
return val.toFixed(num) + '元' // 保留两位小数加个单位'元'
}
}
17.引用外部js
1.方法一
index.js:
export function request () {}
main.js:(全局定义)
import index from 'index' // 注意路径
Vue.prototype.$request = request
别的页面使用时:
this.$request()
2.本地调用json数据:( 一定要放在static文件下)
json.js(格式记得写正确,它是个对象{})
{
"data": [{
"lng": 116.191031,
"lat": 39.988585,
"count": 10
},{
"lng": 116.389275,
"lat": 39.925818,
"count": 11
}]
}
a.vue:(a页面调用,再请求axios)
this.axios('get', url: 'http://localhost:8080/static/json.js').then(res =>{
console.log(res.data.data)
}).catch(err => console.log(err))
3.方法三
util.js:
(function show(){
console.log(45)
})()
main.js:
import 'util'
4.全局过滤器1
filters.js:
const filter = {
filters: { // 过滤器
keepTwoNum (val) {
return val.toFixed(2)
}
}
}
export default filter
child.vue:
import filters from 'filters' // 路径就是filters.js放的位置,这里只是随意一下
filter: [filter] // data同级
{{5.45645 | keepTwoNum}}
5.全局过滤器2
filter.js:
export function toMoney(money = 0){
return money.toFixed(2)
}
.vue:
<div class="goods-price">¥{{goodsPrice | moneyFilter }}</div>
import {toMoney} from 'filter.js'
filters:{
moneyFilter(money) {
return toMoney(money)
}
}
6.全局函数
全局都可以调用的函数(公共通用函数)
global.js:
exports.install = Vue => {
// 比如求json数组中某一个项的平均值
Vue.prototype.getArrAverageItem = (arr, item) => { // item是数组arr中某一项名字
let total = arr.reduce((t, v) => t + (v[item]*10/10), 0)
return (total / arr.length).toFixed(2)
}
}
main.js注册:
import func from './global'
Vue.use(func)
.vue文件上调用:
this.getArrAverageItem(arr, 'number') // 求json数组中所有number的和的平均值
18.那些vue小技巧
1.简写
num: 'num' 简写成 num
(同名时可变一个)
func: (num1, num2) => {
return num1 + num2
}
简写成
func: (num1, num2) => num1 + num2
(返回值只有一行时可省略return)
func: (num) => num
简写成
func: num => num (参数只有一个可以不用括号)
2.$nextTick
Vue中DOM的异步更新时,使用this.$nextTick(() => {})来获取
例子:
<div class="app">
<div ref="msgDiv">{{msg}}</div>
<div>没用$nextTick: {{msg1}}</div>
<div>用了$nextTick: {{msg2}}</div>
<div>没用$nextTick: {{msg3}}</div>
<button @click="changeMsg">
Change the Message
</button>
</div>
new Vue({
el: '.app',
data: {
msg: 'Hello Vue',
msg1: '',
msg2: '',
msg3: ''
},
methods: {
changeMsg() {
this.msg = "Hello world"
this.msg1 = this.$refs.msgDiv.innerHTML
this.$nextTick(() => {
this.msg2 = this.$refs.msgDiv.innerHTML
})
this.msg3 = this.$refs.msgDiv.innerHTML
}
}
})
Hello world
没用$nextTick: Hello Vue
用了$nextTick: Hello world
没用$nextTick: Hello Vue
没用$nextTick时,那msg1和msg3还是原来的值,未曾改变。使用$nextTick后msg2正常的改变了值。
是因为Vue中DOM更新是异步的,所以要使用$nextTick来获取DOM改变后的值
3.ref
vue尽量少操作DOM,这就是和jq最大的区别
<div id="box" ref="box1"></div>
获取上面DOM元素:document.getElementById('box')
而vue使用ref来获取:
this.$refs.box1 或者 this.$refs['box1']
4.keep-alive组件缓存
不希望组件被重新渲染影响使用体验或者避免多次重复渲染降低性能,再或者想要组件缓存下来维持当前的状态。
最常用的就是点击页面后再返回原来的地方而不是像重新进入页面一样,前进刷新而后退不刷新。
keep-alive生命周期钩子函数:activated、deactivated
<keep-alive include="keep-alive">
<!-- 将缓存name为keep-alive的组件 -->
<component>你要缓存的组件</component>
</keep-alive>
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>
export default new Router({
routes: [
{
path: '/',
name: 'Hello',
component: Hello,
meta: {
keepAlive: false // 不需要缓存
}
},
{
path: '/page',
name: 'Page',
component: Page,
meta: {
keepAlive: true // 需要被缓存
}
}
]
})
// B 跳转到 A 时,让 A 缓存,即不刷新
beforeRouteLeave (to, from, next) {
to.meta.keepAlive = true
next()
}
// C 跳转到 A 时让 A 不缓存,即刷新
beforeRouteLeave (to, from, next) {
to.meta.keepAlive = false
next()
}
1.滚动条记录
methods: {
scrollToTop () {
window.scrollTo(0, 0)
}
}
activated () {
this.scrollToTop()
}
2.滚动条记录scrollBehavior(router/index.js)
scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
if (from.meta.keepAlive) {
from.meta.savedPosition = document.body.scrollTop
}
return { x: 0, y: to.meta.savedPosition || 0 }
}
}
5.cookie获取不到
response headers中有set-cookie而request header中不存在cookie
axios.js
axios.create({
withCredentials: true, // 当前请求为跨域类型时是否在请求中协带cookie
})
后端代码:
response.setHeader('Access-Control-Allow-Credentials', true)
response.setHeader('Access-Control-Allow-Origin', 'http://localhost:8080') //http://localhost:8080为你的本地端口
response.setHeader('Set-Cookie', 'token=token_value')
6.slot(插槽)
在父组件中写的内容渲染到子组件里(子组件存放父组件内容)
1.匿名插槽和具名插槽
父组件:
<div>
<child>
<p>父组件中替换子组件插槽</p>
<p slot="slot_name">指定插槽位置</p>
</child>
</div>
import child from 'child'
子组件:
<div>
<slot>(匿名插槽)</slot> // 父组件的内容
<h3>单个插槽</h3>
<slot>(匿名插槽)</slot>
<slot name="slot_name">(具名插槽,绑定name)</slot>
</div>
结果渲染成:
<p>父组件中替换子组件插槽</p>
<h3>单个插槽</h3>
<p>父组件中替换子组件插槽</p>
<p>具名插槽</p>
2.作用域插槽
父组件:
<child :arr="arr">
<div slot="arr_name" slot-scope="arr">{{arr}}</div>
</child>
import child from 'child'
comopenents: {
child
},
data () {
return {
arr: [
{id: 'a1', name: 'aa1',des: 'des1'},
{id: 'a2', name: 'aa2',des: 'des2'},
{id: 'a3', name: 'aa3',des: 'des3'}
]
}
}
子组件:
<div class="city">
<li v-for="item in arr" :key="item.id">
<div class="tableSlot">
<slot name="arr_name" :city="item"></slot>
</div>
</li>
</div>
props: ['arr'] // 接受父组件data中arr数组
渲染结果:
{ "city": { "id": "a1", "name": "aa1", "des": "des1" } }
{ "city": { "id": "a2", "name": "aa2", "des": "des2" } }
{ "city": { "id": "a3", "name": "aa3", "des": "des3" } }
7.合理利用watch的immediate属性
在组件创建时需要获取一次数据列表,然后监听数据的变化,根据数据改变重新获取列表
created () {
this.getData()
},
watch: {
changeValue () {
this.getData()
}
}
简写:
watch: {
changeValue: {
handler: 'getData'
immediate: true // 创建的时候立即执行一次
}
}
8.CSS的scoped私有作用域和深度选择器
私有作用域
<style scoped>
加了scoped,这个页面就变成私有css。没有就变成了全局样式。
</stye>
更改默认样式时(比如elementui样式)改不了可以用深度选择器(>>>)
比如:
.parent >>> .child{}
但是less或者sass等预编译,是不支持>>>操作符的。(/deep/)
比如:
.parent /deep/ .child{}
/deep/ .child{}
9.定时器清除(页面跳转)
方法一:
data: {
return {
timer: null,
num: 0
}
},
mounted () {
this.do()
},
methods: {
do () {
this.timer = setInterval(() => {
this.num++
}, 1000)
}
},
beforeDestroy () {
clearInterval(this.timer)
this.timer = null
this.num = 0
}
方法二:
data: {
return {
num: 0
}
},
mounted () {
this.do()
},
methods: {
do () {
const timer = setInterval(() => {
this.num++
}, 1000)
this.$once('hook:beforeDestroy', () => { // 通过$once来监听定时器,在beforeDestroy钩子可以被清除。
clearInterval(timer)
this.num = 0
})
}
}
10.动态监听某一个值变化触发函数
1.computed和watch结合
computed: {
currVal () {
return this.$store.state.val // 计算当前vuex中this.$store.state.val变化的值
}
},
watch: {
currVal (newVal, oldVal) {
this.init()
}
},
methods: {
init () {}
}
2.直接watch
watch: {
'$store.state.val': function(newVal, oldVal) { // 不能再用this来获取$store.state.val,并且用function函数形式
this.init()
}
},
methods: {
init () {}
}
3.监听vuex使用模块化
computed:{
...mapGetters([ 'val' ])
},
watch: {
val: function(newVal) {
this.init()
}
},
methods: {
init () {}
}
11.watch立即执行(组件创建后 watch 能够立即执行)
data() {
return {
name: 'jkl'
}
},
watch: {
name: {
handler: 'getList',
immediate: true
}
},
methods: {
getList() {
console.log(this.name)
}
}
12.移动端实现双击事件(dblclick)
<button @click="dbClick">双击</button>
data: {
index: 0
},
methods: {
dbClick () {
this.index++
setTimeout(() => this.index = 0}, 500) // 0.5s后清零
if (this.index > 1) {
console.log('您已经双击了按钮!')
this.index = 0
}
}
}
13.移动端使用vConsole真机调试(像web端控制台)
npm install vconsole
main.js中
import Vconsole from 'vconsole'
const vConsole = new Vconsole()
Vue.use(vConsole)
使用: 在你需要的地方使用console.log()就行
14.加密解密(对登录保存密码进行加密)
安装:npm install crypto-js
main.js:
import CryptoJS from "crypto-js"
加密(secretkey123加密解密的钥匙,要统一就行(还可以secretkey12等等))
let encryptVal = CryptoJS.AES.encrypt('密码', 'secretkey123').toString()
解密
let decryptVal = CryptoJS.AES.decrypt(encryptVal, 'secretkey123').toString(CryptoJS.enc.Utf8)
15.Vue项目中的BASE_URL解析
比如:<script src="<%= BASE_URL %>js/liveplayer-lib.min.js"></script>
webpack.dev.conf.js:
const devWebpackConfig = merge(baseWebpackConfig, {
plugins: [
new webpack.DefinePlugin({
'process.env': require('../config/dev.env'),
BASE_URL: '"/"'
})
]
}
16.动态引入图片
一般:
<img src="../assets/logo.png" >
动态(适用于多次使用或者多次更改图片位置):
<img :src="logo">
import logo from '../assets/logo.png'
data() {
return {
logo
}
}
或者
<img :src="logo">
data() {
return {
logo: require('../assets/logo.png')
}
}
17.父组件请求的数据传给子组件
因为ajax是异步请求所以没办法一下子传给子组件参数
1.用v-if来判断下是否显示
<div v-if="isList"></div>
props: {
list: Array
},
computed: {
isList () {
return this.list.length
}
}
2.用监听watch再赋值
props: {
list: Array
},
watch: {
list(val) {
this.list = val
}
}
18.router-link添加点击事件(.native)
类似还有很多非原生标签
<router-link class="second-item" v-for="item in menu" :to="item.link" :key='item.id' @click.native="selectNav(j)">
{{ obj.name }}
</router-link>
19.管理平台有折叠按钮导致echarts变形
npm install element-resize-detector --save
main.js:
import ElementResizeDetectorMaker from "element-resize-detector"
Vue.prototype.$erd = ElementResizeDetectorMaker()
使用:
this.$erd.listenTo(document.getElementById("你取的id"), (element) => {
this.$nextTick(() => {
this.pie.resize() // 你echarts初始化变量 (this.pie = echarts.init(this.$refs.pie))
})
})
19.vuex
Vuex是Vue.js应用程序的状态管理模式+库。 从组件中提取共享状态,并在全局单例中进行管理,更多的结构和可维护性。
npm install vuex –-save 安装,安装好后在src下新建store目录并在其里面新建index.js文件配置vuex
vuex配置
main.js:
import store from './store/index'
new Vue({
el: '#app',
router,
store,
components: { App },
template: '<App/>'
})
store/index.js:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state,
getters,
mutations,
actions
})
export default store
1.state
const store = new Vuex.Store({
state: { // 访问状态对象,存变量(状态)相当于data
count: '', // this.$store.state.count获取,页面上不用this
name: ''
}
})
2.getters
const store = new Vuex.Store({
getters: { // 获取者,相当于计算属性
getAdmin: state => { // this.$store.commit('getAdmin')
return state.count
},
getName: state => state.name // 使用箭头函数效果
}
})
3.mutations
官方推荐mutations里的方法大写
const store = new Vuex.Store({
mutations: { // 触发状态,同步函数,修改store中的值 this.$store.commit('INCREMENT_ADD')
INCREMENT_ADD (state) {
return state.count
},
PARAMS: (state, params) => { // this.$store.commit('PARAMS', {num: 10}) // 此时的params就是{num: 10}
state.count += params.num
}
}
})
4.actions
Action 提交的是 mutation,而不是直接变更状态。可以包含任意异步操作。
const store = new Vuex.Store({
mutations: {
INCREMENT_ADD (state) {
return state.count
}
},
actions: {
FUNC: context => { // 异步函数,相当于methods this.$store.dispatch('FUNC')
context.commit('INCREMENT_ADD')
},
FUNC1: ({ commit }) { // 上面的简写
commit('increment')
}
}
})
5.模块化(简洁vuex)
vuex中函数多了就可以用模块化,看个人
import {mapState, mapMutations, mapGetters, mapActions} from 'vuex' // 引用模块化,要使用哪个就引用哪个
比如mapState
一般的时候
computed: {
count () {
return this.$store.state.num
}
}
模块化的时候
扩展运算符 ... 用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中(类似于浅拷贝,当原对象发生变化的时候,拷贝对象也跟着变化)
let obj1 = { a: 1, b: 2 }
let obj2 = { ...obj1 } // { a: 1, b: 2 }
// Object.assign方法用于对象的合并,将源对象的所有可枚举属性,复制到目标对象中
let obj3 = Object.assign({}, obj1) // { a: 1, b: 2 }
// 如果用户自定义的属性,放在扩展运算符后面,则扩展运算符内部的同名属性会被覆盖掉
let obj4 = {...obj1, ...{a:2, b: 4}} // {a: 2, b: 4}
let arr1 = [4, 3, 6]
console.log([...arr1]) // [4, 3, 6] // Array.prototype.slice.call(arr1)
console.log([...'hello']) // [ "h", "e", "l", "l", "o" ]
computed: {
...mapState({
count: state => state.num
})
}
或者(不对数据进行计算,单纯的就是取值而已)
computed: {
...mapState([ // 数组形式 'count' ]),
再或者:
...mapState({ // json形式
count: num
})
}
要懂vuex还是官方好: vuex官方文档
20.vue常见报错
1.v-for(key)
Duplicate keys detected: '1'. This may cause an update error.
这是v-for循环中key值重复
2.Navigating to current location ("/xxx") is not allowed
在路由中添加了相同的路由,需要重写路由的push方法
router/index.js:(路由文件下添加)
const routerPush = Router.prototype.push
Router.prototype.push = function push(location) {
return routerPush.call(this, location).catch(error=> error)
}
3.点击form中的button会刷新页面
更换button标签或者点击事件添加个.prevent // 阻止冒泡事件
<button @click.prevent="login">登录</button>
4.npm 安装出错 npm ERR! request to registry.npmjs.org/express failed, reason: unable to verify the first certificate
你用了http协议,关闭npm的https就好了。
npm config set strict-ssl false
5.request to registry.npmjs.org/hls.js failed, reason: connect ETIMEDOUT 104.16.24.35:443, this is a problem related to network connectivity.
安装插件时使用npm i而不是npm install造成。有些可以,我电脑重装后可能少配置了什么东西。还是整体用npm install安装吧
比如:
npm i hls.js -S
换成
npm install hls.js -S
6.vue.esm.js?f959:610 [Vue warn]: Failed to mount component: template or render function not defined.
这是.vue文件中div最外部没有<template></template>包裹起来
正确:
<template>
<div></div>
</template>
错误:
<template>
<div></div>
<div></div>
</template>
7.Cannot find module '../config'问题
config目录下缺少了index.js文件导致的
8.npm run dev 页面出现了 Cannot GET /
config/index.js文件中
dev: {
assetsPublicPath: './' //改成 assetsPublicPath: '/',
}
9.Error: timeout of 1000ms exceeded
请求超时,配置axios中由于设置默认timeout 1000,但实际请求已经过了1000ms。可以设置时间长点
axios.defaults.timeout = 5000
或者:
axios.create({
timeout: 5000
)}
10.'webpack-dev-server' 不是内部或外部命令,也不是可运行的程序
删除依赖包node_modules重新npm install可解决大部分依赖问题
版本有问题,重新下载可以的版本(比如我现在用的2.9.1版本可行)
方法一:
1.删除node_modules
2.找到package.json下的"webpack-dev-server": "^1.9.1" 手动改成"webpack-dev-server": "^2.9.1"
3.重新npm install
方法二:
1.npm uninstall webpack-dev-server
2.npm install webpack-dev-server@2.9.1 --save
11.vue的方法格式写错
根据提示是taskList.vue页面出现问题:
mounted: {} => mounted () {}
12.请求加token变成option请求
报错: Request header field token is not allowed by Access-Control-Allow-Headers in preflight response
解决:
后端设置允许添加头部
response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization"); //这里“Authorization”是我要传到后台的内容key
13.element分页错误
vue.esm.js? [Vue warn]: Invalid prop: custom validator check failed for prop "pagerCount".
分页中有这配置:
:pager-count="5" // 只能5~21之间的奇数(奇数,奇数,奇数)
14.路由history模式刷新出现404问题
默认路由mode:hash不存在这问题(就是地址栏有#)
方法一: 服务器配置如果URL匹配不到任何静态资源,就跳转到默认的index.html
location /{
root /data/nginx/html;
index index.html index.htm;
error_page 404 /index.html;
}
方法二: 后端配置例子
15.打包报错Cannot find module 'compression-webpack-plugin
config/index.js
build:{
productionGzip: true // gzip压缩开启(打包压缩文件用于优化,减少代码体积)
}
但要安装compression-webpack-plugin
npm install --save-dev compression-webpack-plugin@1.1.12
这个是我的版本,根据你的webpack和node来安装,可能因为webpack版本太高而无法安装成功。
可查看官网来安装。 compression-webpack-plugin官网
16.[WDS] Disconnected
端口被别的项目占用了,所以导致项目出现了这个问题。更改项目的端口,或者只开一个项目。
17.BASE_URL is defined
const devWebpackConfig = merge(baseWebpackConfig, {
plugins: [
new webpack.DefinePlugin({
'process.env': require('../config/dev.env'),
// BASE_URL: '"/"' // <%= BASE_URL %>
})
]
)}
index.html:
<!-- <script src="<%= BASE_URL %>js/liveplayer-lib.min.js"></script> -->
删除这句话用别的路径代替,注释都不要,不然打包会报错
18.更改数组中属性,在页面中不生效的解决方法
比如从后端接受到的数据数组为[{name: '小明'}, {name: '小红'}],
而实际上你需要的却是这样的[{name: '小明', show: false}, {name: '小红', show: false}]
在页面上不管点击也好鼠标移入移出也好。都要遵循先为接收到的数据初始化show属性,再将处理后的数据赋值给vue的data中。
比如:
this.$axios.get(this.api.list).then(res => {
res.lists.forEach(e => e.show = false)
this.lists = res.lists
})
this.$nextTick(() => {
// DOM结构发生变化时要添加这个
})
this.$forceUpdate() // 强制更新
19.路由跳转问题:Uncaught (in promise) Error: Navigation cancelled from “/...“ to “/...“ with a new navigation.
在router.js文件下:
import Router from 'vue-router'
Vue.use(Router)
// 解决编程式路由往同一地址跳转时会报错的情况
const originalPush = Router.prototype.push
const originalReplace = Router.prototype.replace
Router.prototype.push = function push(location, onResolve, onReject) {
if (onResolve || onReject) return originalPush.call(this, location, onResolve, onReject)
return originalPush.call(this, location).catch(err => err)
}
Router.prototype.replace = function push(location, onResolve, onReject) {
if (onResolve || onReject) return originalReplace.call(this, location, onResolve, onReject)
return originalReplace.call(this, location).catch(err => err)
}
21.番外篇
1.es6
2.css垂直居中
1.方法一
position: relative;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
2.方法二
position: relative;
position: absolute;
top: 50%;
left: 50%;
width: 80px;
height: 80px;
margin: -40px -40px; /* 知道宽高情况下,一半 */
3.方法三
display: flex;
justify-content: center;
align-items: center;
4.完整的css和html(虽然懂一两个就够了,但面试官比较喜欢问这题)
<style>
*{margin: 0;padding: 0;box-sizing: border-box;}
.main{
position: relative;
width: 100px;
height: 100px;
border: 1px solid gray;
}
.box{
border: 1px solid green;
}
.vc1{
position: absolute;
top: 50%;
left: 50%;
width: 80px;
height: 80px;
transform: translate(-50%, -50%); /* 平移 */
}
.vc2{
position: absolute;
top: 50%;
left: 50%;
width: 80px;
height: 80px;
margin: -40px -40px; /*宽度和高度的一半*/
}
.flex{
display: flex; /* flex法,最简单粗暴有效,移动端必选 */
justify-content: center;
align-items: center;
}
.vc3{
width: 80px;
height: 80px;
}
.vc4{
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto; // 当所有定位位置设置0时,margin自动垂直居中
width: 80px;
height: 80px;
}
.table{
display: table;
width: 200px;
height: 200px;
border: 1px solid gray;
}
.vc5{
display: table-cell; /* 表格法,知道下原理就行,不采用此方法 */
border: 1px solid green;
vertical-align: middle;
text-align: center;
}
.vc6fa{
text-align: center;
}
.vc6fa::before{
display: inline-block;
content: '';
height: 100%;
vertical-align: middle;
}
.vc6{
display: inline-block; /* 行内块元素法,要注意两个行内块一起时出现一上一下,使用vertical-align控制 */
width: 80px;
height: 80px;
vertical-align: middle;
}
</style>
<div class="main">
<div class="vc1 box">方法一</div>
</div>
<div class="main">
<div class="vc2 box">方法二</div>
</div>
<div class="main flex">
<div class="vc3 box">方法三</div>
</div>
<div class="main">
<div class="vc4 box">方法四</div>
</div>
<div class="table">
<div class="vc5 box">方法五</div>
</div>
<div class="main vc6fa">
<div class="vc6 box">方法六</div>
</div>
3.整体ui布局
1.双列布局
一列固定宽度,另外一列自适应宽度
2.两边固定中间自适应
<style>
*{margin: 0;padding: 0;}
.main{width: 100vw;height: 100vh;display: flex;justify-content: space-around;}
.div1,.div3{min-width: 100px;height: 100vh;background: green;}
.div2{width: 100%;height: 100vh;background: grey;}
</style>
<div class="main">
<div class="div1">1</div>
<div class="div2">2</div>
<div class="div3">3</div>
</div>