vueJs
01 项目搭建
1.1 安装手架与创建项目
npm install -g @vue/cli // 安装vue-cli 3.0
vue create name // 创建项目
1.2 创建配置
模式配置
Vue CLI v3.0.0-beta.15
? Please pick a preset:
default (babel,eslint) // 默认
> Manually select features // 自定义模式
插件安装配置
Vue CLI v3.0.0-beta.15
? Please pick a preset: Manually select features // 之前选择的自定义模式
? Check the features needed for your project:
(*) Babel // Babel编译
( ) TypeScript
( ) Projectressive Web App (PWA) Support
(*) Router // 路由
(*) Vuex // 状态管理器
(*) CSS Pre-processors // CSS预处理器
(*) Linter / Formatter // 代码检测和格式化
( ) Unit Testing // 以及单元测试
( ) E2E Testing // 暂时不考虑端到端测试(E2E Testing)
css预处理语言
此处选择 LESS
Vue CLI v3.0.0-beta.15
? Please pick a preset: Manually select features // 之前选择的自定义模式
? Check the features needed for your project:
Babel,Router,Vuex,CSS Pre-processors,Linter / Formatter,Unit Testing
?Pick a Css pre-processor (PostCss,Autoprefixer and CSS Modules are supported by default):
SCSS / SASS
> LESS
Stylus
ESLint的代码规范
此处使用 Standard代码规范
Vue CLI v3.0.0-beta.15
? Please pick a preset: Manually select features // 之前选择的自定义模式
? Check the features needed for your project:
Babel,Router,Vuex,CSS Pre-processors,Linter / Formatter,Unit Testing
?Pick a Css pre-processor (PostCss,Autoprefixer and CSS Modules are supported by default):LESS
? Pick a linter / formatter config:
> ESLint with error prevention only
ESLint + Airbnb config
ESLint + Standard config
ESLint + Prettier
何时进行代码检测
此处选择在保存时进行检测
Vue CLI v3.0.0-beta.15
? Please pick a preset: Manually select features // 之前选择的自定义模式
? Check the features needed for your project:
Babel,Router,Vuex,CSS Pre-processors,Linter / Formatter,Unit Testing
?Pick a Css pre-processor (PostCss,Autoprefixer and CSS Modules are supported by default):LESS
? Pick a linter / formatter config: Standard
? Pick additional lint features:
> (*) Lint on save
( ) Lint and fix on commit
单元测试解决方案
此处选择 Jest
Vue CLI v3.0.0-beta.15
? Please pick a preset: Manually select features // 之前选择的自定义模式
? Check the features needed for your project:
Babel,Router,Vuex,CSS Pre-processors,Linter / Formatter,Unit Testing
?Pick a Css pre-processor (PostCss,Autoprefixer and CSS Modules are supported by default):LESS
? Pick a linter / formatter config: Standard
? Pick additional lint features: Lint on save
? Pick a unit testing soution:
Mocha + chai
> Jest
配置文件存放位置
此处选择单独保存在各自的配置文件中
Vue CLI v3.0.0-beta.15
? Please pick a preset: Manually select features // 之前选择的自定义模式
? Check the features needed for your project:
Babel,Router,Vuex,CSS Pre-processors,Linter / Formatter,Unit Testing
?Pick a Css pre-processor (PostCss,Autoprefixer and CSS Modules are supported by default):LESS
? Pick a linter / formatter config: Standard
? Pick additional lint features: Lint on save
? Pick a unit testing soution: Jest
? Where do you prefer placing config for Babel,PostCSS,ESLint,etc.?
> In dedicated config files
In package.json
1.3 CLI 环境变量
1.3.1 设置环境变量
cli-3.0总共提供了四种方式来制定环境变量:
- 在根目录添加
.env
文件,配置所有情况下都会用到的配置(不知道这个存在的意义,所有的都需要的也就不需要配置了吧)。 - 在根目录添加
.env.local
文件,配置所有情况下都会用到的配置,与.env
的区别是只会在本地,该文件不会被git跟踪。 - 在根目录添加
.env.[mode]
文件,配置对应某个模式下的配置,比如:.env.development来配置开发环境的配置。 - 在根目录添加
.env.[mode].local
文件,配置对应某个模式下的配置,与.env.[mode]
的区别也只是会在本地生效,该文件不会被git跟踪。
在文件中,我们只需要以key=value的方式就可以设置变量了。
//例如
FOO=bar
VUE_APP_SECRET=secret
1.3.2 使用环境变量
设置完环境变量之后就可以在我们的项目中使用这两个变量了。
不过还需要注意的是在项目的不同地方使用,限制也不一样。
- 在项目中,也就是src中使用环境变量的话,必须以 **VUE_APP_ **开头。例如我们可以在main.js中直接输出:
console.log(process.env.VUE_APP_SECRET)
- 在webpack配置中使用,没什么限制,可以直接通过 process.env.XXX 来使用
1.3.3 模式
模式是Vue CLI项目中的一个重要概念。默认情况下,Vue CLI项目中有三种模式:
- development:在
vue-cli-service serve
下,即开发环境使用 - production:在
vue-cli-service build
和vue-cli-service test:e2e
下,即正式环境使用 - test: 在
vue-cli-service test:unit
下使用
另外,如果你想要修改模式下默认的环境变量的话可以通过–mode来实现,例如:
"dev-build": "vue-cli-service build --mode development"
1.3.4 实例
// .env.local
VUE_APP_SECRET=''
// .env.development
VUE_APP_SECRET=''
// .env.online
VUE_APP_SECRET=''
// package.json
"serve": "vue-cli-service serve",
"local": "vue-cli-service build --mode local",
"build": "vue-cli-service build --mode online",
"lint": "vue-cli-service lint"
1.4 跨域
// vue.config.js
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://xxxx/device/', //对应自己的接口
changeOrigin: true,
ws: true,
pathRewrite: {
'^/api': '' // 请求接口以/api开头
}
}
}
}
}
1.5 外部ip访问项目
// package.json 允许任何域名访问
"serve": "vue-cli-service serve --host 0.0.0.0"
1.6 配置vue.config.js
const path = require('path')
const resolve = dir => {
return path.join(__dirname, dir)
}
module.exports = {
/** 区分打包环境与开发环境
* process.env.NODE_ENV==='production' (打包环境)
* process.env.NODE_ENV==='development' (开发环境)
* baseUrl: process.env.NODE_ENV==='production'?"https://cdn.didabisai.com/front/":'front/',
*/
// 项目部署的基础路径
// 我们默认假设你的应用将会部署在域名的根部,
// 例如 https://www.my-app.com/
// 如果你的应用部署在一个子路径下,那么你需要在这里
// 指定子路径。比如将你的应用部署在
// https://www.foobar.com/my-app/
// 那么将这个值改为 '/my-app/'
// publicPath:'', // 二级目录名称
outputDir: "dist", // 打包文件夹名称
// 不检测语法
lintOnSave: false, // 使用带有浏览器内编译器的完整构建版本 // https://vuejs.org/v2/guide/installation.html#Runtime-Compiler-vs-Runtime-only
runtimeCompiler: false, // babel-loader默认会跳过`node_modules`依赖. // 通过这个选项可以显示转译一个依赖
transpileDependencies: [
/* string or regex */
], // 是否为生产环境构建生成sourceMap?
productionSourceMap: false, // 调整内部的webpack配置. // see https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md
// 快捷配置
chainWebpack: config => {
config.resolve.alias
.set('@', resolve('src'))
// 导入直接 @images
.set('@images', resolve('src/assets/images'))
// 图片获取路径 src="@images/login/logo.png"
},
configureWebpack: () => {}, // CSS 相关选项
css: {
// 将组件内部的css提取到一个单独的css文件(只用在生产环境)
// 也可以是传递给 extract-text-webpack-plugin 的选项对象
extract: true, // 允许生成 CSS source maps?
sourceMap: false, // pass custom options to pre-processor loaders. e.g. to pass options to // sass-loader, use { sass: { ... } }
loaderOptions: {}, // Enable CSS modules for all css / pre-processor files. // This option does not affect *.vue files.
modules: false
}, // use thread-loader for babel & TS in production build // enabled by default if the machine has more than 1 cores
pwa: {}, // configure webpack-dev-server behavior
devServer: {
open: process.platform === "darwin",
disableHostCheck: false,
host: "0.0.0.0",
port: 8080,
https: false,
hotOnly: false, // See https://github.com/vuejs/vue-cli/blob/dev/docs/cli-service.md#configuring-proxy
proxy: {
'/api': {
target: 'http://xxx.xx.x.x:7788/',
changeOrigin: true,
ws: true,
pathRewrite: {
'^/api': '/'
}
}
}
// before: app => {}
},
// 第三方插件配置
pluginOptions: {
// ...
}
}
// api
import request from '@/tools/request/request'
export function test(params) {
return request.post(
'ews2/add'
)
}
1.7 配置自由地址
// service/common/serverAddr
let localServerAddr = '/api/' //本地服务器地址
//let localServerAddr = 'http://x.x.x.x/' //预上线地址
// let LongServerAddr = 'https://xx.xx-info.xx/' //远程服务器地址
// 服务器地址
function getAddr(){
// 本地使用指定服务器
if(
/http:\/\/localhost/ig.test(window.location.href)
|| /192.168.8.76/ig.test(window.location.href)
){
return localServerAddr
}
return `${window.location.protocol}//${window.location.host}/`
}
export default getAddr()
02 项目结构
2.1 目录结构视图
src
// 接口文件夹
-api
// 静态文件 (img、css、js、其他)
-assets
// 全局公共组件
-components
// 全局过滤文件
-filter
// 本地存储
-localStorageService
-key // 本地值名称
-localStorageService // 本地存储方法
// 路由文件
-router
-routes // 所有路由地址
-index // 路由配置
// 同级组件传值使用
-sameLeve
//... 自己定义文件
// 系统服务文件
-service
// 配置信息
-common
-serverAddr // 获取服务器地址前缀
-variable // 配置vue全局使用数据
// 外部使用方法与定义数据文件
-data
// vuex
-store
-modules // 定义的模块vuex
-mutationFn_name // 定义vuex的名称
// 工具
-tools
-regular // 方法处理封装
-request // 请求封装
-weChat // 微信调用方法封装
// iview框架方法封装
-vuxUI
-base // 封装部分弹层方法
2.2 axios请求封装
npm i axios // 安装插件
import axios from 'axios';
import Cookies from 'js-cookie'
import serverAddr from '@/service/common/serverAddr'
let instance = axios.create({
baseURL: serverAddr, // 地址 ,需要用时切换相应的地址
timeout: 3000, // 请求超时
})
// post请求头的设置
instance.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8'
// 添加请求拦截器
instance.interceptors.request.use(
config=>{
// 在发送请求之前做些什么
let token = Cookies.get('token')
token && (config.headers.token = token)
return config;
},
error=> {
// 对请求错误做些什么
return Promise.reject(error);
}
)
// 添加响应拦截器
instance.interceptors.response.use(
response=> {
// 对响应数据做点什么
// console.log(response.data.msg)
if(response.data.code === 0){
return Promise.resolve(response)
}else{
return Promise.reject(response)
}
},
error=>{
// 对响应错误做点什么
const responseCode = error.response.status
switch (responseCode) {
// 401:未登录
case 401:
break
// 404请求不存在
case 404:
// 弹窗
alert('网络请求不存在')
break
default:
alert(error.response.data.message)
}
return Promise.reject(error)
}
)
/**
* 使用es6中的类,进行简单封装
*/
class request {
get(url, params) {
return new Promise((resolve, reject) =>{
instance.get(url,{params})
.then(result=>resolve(result))
.catch(err=>reject(err))
})
}
post(url, params) {
return new Promise((resolve, reject) =>{
instance.post(url,params)
.then(result=>resolve(result))
.catch(err=>reject(err))
})
}
}
export default request;
2.3 本地存储封装
import { isIframe } from '@/tools/regular/regular'
/***
* 数组数据去重
* @param arr
* @return {Array}
*/
function removeArr( arr ){
let temp = {};
let newArr = [];
for(let i=0;i<arr.length;i++ ){
if( !temp[arr[i]] ){
temp[arr[i]]= true;
newArr.push( arr[i] );
}
}
return newArr;
}
class localStorageService {
constructor(){
let keyArr = window.localStorage.getItem('keyArr')
if(keyArr){
this.arr = removeArr(JSON.parse(keyArr))
}else{
this.arr = []
window.localStorage.setItem('keyArr',JSON.stringify(this.arr))
}
}
/***
* 启动清理器
*/
startClear(){
// console.log(this.arr)
this.cleanBeOverdue()
}
/**
* 清除过期本地存储
*/
cleanBeOverdue() {
//不存在本地存储,继续轮询
// console.log(this.arr)
if(this.arr.length===0){
return false
}
let nowTime = (new Date()).getTime();
for (let i=0;i<this.arr.length;i++){
let key = this.arr[i]
let item = window.localStorage.getItem(key)
item = JSON.parse(item)
// 存在时间
if(typeof item === 'Object' && item.time){
//删除到期本地存储
if(nowTime*1 >= item.time*1){
this.remove(key)
}
}
}
setTimeout(()=>{
this.cleanBeOverdue()
},1000)
}
/**
* 设置本地存储
* @param key 键名
* @param value 值
* @param time 存储时间 (毫秒)
*/
set(key,value,time=1000*60*60*24*7){
let futureTime = (new Date()).getTime()*1 + time; //到期时间
let data = {
data:value,
time:futureTime
};
data = JSON.stringify(data);
window.localStorage.setItem(key,data)
// 存储本地
this.arr.push(key)
window.localStorage.setItem('keyArr',JSON.stringify(removeArr(this.arr)))
}
/**
* 获取本地存储
* @param key 键名
*/
get(key){
let getValue = window.localStorage.getItem(key);
return getValue==null?null:JSON.parse(getValue).data;
}
/**
* 获取iframe父级本地存储
* @param key 键名
*/
getParent(key){
if(!isIframe()) return null // 不在iframe里
let getValue = parent.localStorage.getItem(key);
return getValue==null?null:JSON.parse(getValue).data;
}
/**
* 删除某个存储
* @param key 键名
*/
remove(key){
window.localStorage.removeItem(key)
for (let i = 0;i< this.arr.length;i++){
let item = this.arr[i]
if(String(item) === String(key) ){
this.arr.splice(i,1)
}
}
window.localStorage.setItem('keyArr',JSON.stringify(this.arr))
}
/**
* 删除所有存储
*/
clearAll(){
window.localStorage.clear()
}
}
export default new localStorageService();
2.4 全局参数配置
import { USER_INFO } from '@/localStorageService/key'
import localStorageService from '@/localStorageService/localStorageService'
const config = {
install(Vue, options) {
/**
* 图片地址
* @param url
* @return {string}
*/
Vue.prototype.$imgUrl = url=>'/api'+url
/**
* 获取用户信息
*/
Vue.prototype.$userInfo = ()=>localStorageService.get(USER_INFO)
}
}
export default config
2.5 全局过滤方法
import Vue from 'vue'
Vue.filter('comVal', val=>{
return val?val:'-'
})
2.6 vuex
2.6.1 安装与配置
npm i -S vuex
npm i -S vuex-persistedstate # 对数据进行缓存
// vuex/index
import Vue from 'vue'
import Vuex from 'vuex'
//”vuex”:”^3.0.1”,运用”vuex-persistedstate”来对数据进行缓存
import createPersistedState from 'vuex-persistedstate'
Vue.use(Vuex)
const module = {
// data 存储数据
state: {},
//过滤 filter
getters: {},
// 操控 state 里面的数据的 methods
mutations: {},
// 调用 mutations 里的方法,可以进行异步操作
actions:{},
// 数据进行缓存
plugins: [createPersistedState()]
}
export default new Vuex.Store(module)
import store from './vuex/index'
Vue.config.productionTip = false;
new Vue({
el: '#app',
router,
store, //使用store
// 传递一个参数: 让页面默认显示的模板 return
render: h => h(App)
})
2.6.2 vuex的使用
定义规则
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
// data 存储数据
const state = {
age: [
{name: '1', age: 1},
{name: '2', age: 2},
{name: '3', age: 3},
{name: '4', age: 4}
],
name: 'abc',
vxcart: []
}
// 相当于 computed 作用于 state 过滤操作 .....
const getters = {
guolv (state) {
state.name = '123'
}
}
// 主要是用来操控 state 里面的数据的
const mutations = {
addage () {
state.age++
},
minusage () {
state.age--
},
vxaddCart (state, value) {
state.vxcart.push(value)
},
vxjian (state, value) {
state.vxcart.forEach((val, index) => {
if (val.title === value.title) {
state.vxcart.splice(index, 1)
}
})
}
}
// 来调用 mutations 里的方法,可以进行异步操作
// 通过actions进行commit提交给mutation,再进行数据操作 , 例如发送请求
const actions = {
// 默认接收了一个参数
addagepro ({commit},value) {
//参数使用mutations的里面的函数
// addage是里面的参数,value需要存储的值
commit('addage',value) //调用mutations里的addage函数
}
}
// 语法检测的时候 new必须进行赋值
/* eslint-disable no-new */
export default new Vuex.Store({
// 是用来保存数据的
state,
mutations,
actions,
getters
})
使用方法
<template>
<div id="app">
<!-- store显示的数据 -->
{{ $store.state }}
<button @click="addage()">+</button>
<button @click="minusage()">-</button>
<!-- 路由所显示的地方 -->
<router-view :aa="aaaa"></router-view>
<!-- 直接用state的值 -->
{{age}}
</div>
</template>
<script>
// 接收的参数的格式 map+驼峰命令
// state = mapState
import {mapState, mapMutations, mapActions, mapGetters} from 'vuex'
import header from './components/hreder.vue'
export default {
name: 'app',
computed: {
//解构state 相当于把里面的name、age放在data里,可以直接用
...mapState([
'name',
'age'
]),
...mapGetters([
//...
])
},
methods: {
//接收 mutations
...mapMutations([
//接收里面的方法有哪些
'addage',
'minusage'
]),
getState(){
//获取state的状态 只可以读,不可改
console.log(this.$store.state)
//修改state状态 key ,value
this.$store.commit('name',{name: 'kuke_kuke'})
}
}
}
</script>
2.6.3 同步异步调用
/*
dispatch:含有异步操作,例如向后台提交数据,写法: this.$store.dispatch('mutations方法名',值)
commit:同步操作,写法:this.$store.commit('mutations方法名',值)
*/
this.$store.commit('toShowLoginDialog', true);
this.$store.dispatch('toShowLoginDialog',false)
2.6.4 模块化
定义规则
// vuex/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import market from "./modules/market/market";
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
market
}
}
)
//vuex/modules/mutationFn_name.js 设置调起变量函数名称
import { SET_MARKET_COUNT } from "../mutationFn_name";
import { getMarketingCount } from '@/api/market/market';//接口
export default {
namespaced: true,
state: {
count: 0
},
actions: {
getMarketingCount({commit}, params) {
return new Promise((resolve, reject) => {
getMarketingCount(params,{loading:{auto:false}})
.then(resp=>{
let data = resp.body;
commit(SET_MARKET_COUNT, data.marketingCount)
})
.catch(e=>console.log(e));
})
}
},
mutations: {
[SET_MARKET_COUNT](state, data) {
state.count = data
}
},
getters: {
getMarketCount: state => {
return state.count
},
}
}
调用方法
// App.vue 调起函数
getMark_num(){
let userInfo = localStorageService.getObj(SR_USER_INFO);
// userInfo = '13533282209'; // local
userInfo = '13926264396'; //demo
let params = {
account:userInfo,
calc_date:'2018002'
};
// market下的getMarketingCount函数
this.$store.dispatch('market/getMarketingCount',params);
// 不开启命名空间 ,直接写 actions 下的userLogin函数名
this.$store.dispatch('userLogin', param);
}
调用完方法后,同级组件数值发生变化
// 其他.vue
import {mapActions, mapGetters} from 'vuex'
computed: {
mark_num() {
const marketCount = this.getMarketCount();
return marketCount;
}
},
methods: {
// 显示的数据
...mapGetters({
// 函数名 getMarketCount
getMarketCount: 'market/getMarketCount', //调起 marke下getMarketCount函数
})
}
2.7 路由
2.7.1 安装
npm i vue-router
2.7.2 模块化
// router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import iView from 'iview';
// vuex获取本地存储
import store from '@/store/index.js'
// 路由模版
import article from './routes/article';
import user from './routes/user';
Vue.use(Router)
let RouterFn = new Router({
routes:routersArr()
})
// 全局路由钩子函数 对全局有效
RouterFn.beforeEach((to, from, next) => {
let auth = to.meta.auth;//路由页面进入权限
let token = store.getters['user/getToken']; // 判断是否登陆
// console.log(token)
if (auth) {
// 需要登录才有权限进入
if(token){
// 已经登录 继续走
next()
}else{
// 未登录
iView.Message.warning({
content:'该页面需要登录才能进入'
})
}
} else {
// 不需要权限的页面
next()
}
})
export default RouterFn;
function routersArr() {
let routes = article
.concat(user)
;
return routes
}
// router/routes/article.js
export default [
{
path:'/articleNote',
name:'articleNote',
// 路由meta传参
meta:{
auth:true,// 需要登录才能进入
hideFooter:true
},
component:()=>import('../../views/Article/ArticleNote.vue'),
},
]
import Vue from 'vue'
import App from './App.vue'
// 导入路由
import router from './router/index'
Vue.config.productionTip = false
new Vue({
router,
render: h => h(App)
}).$mount('#app')
2.7.3 子路由
const router = new Router({
routes: [
{path: '/', component: index},
{
path: '/a',
component: a,
children: [// 子路由
{path: 'aaa', component: aa},
{path: ':id', component: aa},
/*
以“/”开头的嵌套路径会被当作根路径,所以子路由上不用加“/”;在生成路由时,主路由上的path会被自动添加到子路由之前,所以子路由上的path不用在重新声明主路由上的path了。
*/
{
path: 'collection',
name: 'Collection',
component: Collection
},
]
},
// /b/ bb
{path: '/b/:id', name: 'b',omponent: b},
]
})
2.7.4 router-link导航标签
属性:to 、replace、 append、 tag、 active-class、 exact 、 event、 exact-active-class
to (必选参数): url
<!-- 静态绑定 -->
<router-link to="home">Home</router-link>
<!-- 动态绑定 -->
<router-link :to="{ path: '/home' }">Home</router-link>
tag 把 渲染成某种标签,例如
-
<!-- 渲染为Li --> <router-link :to="'index'" tag="li" >Home</router-link>
exact 严格模式
<li><router-link to="/" exact>严格匹配</router-link></li>
active-class 链接激活时的样式
<template> <!-- 路由url与to路径匹配时激活class --> <router-link to="/about" active-class="activeClass" >about</router-link> </template> <style> .router-link-active{ color:red; } </styles>
2.7.5 路由跳转与传参
一、标签传参
<router-link :to="{ name:'b',query:{ id:123 } }">/b/bb</router-link> <router-link :to="{ path:'/b/bb',params:{ id:123 } }">/b/bb</router-link>
二、路由方法传参
// 路由跳转跳转 this.$router.push({ path:'/path', //路径 name:'pathName', //配置路由时的name // 传参params params:{ title:'title', info:'info' }, // 传参query query : { title:'title', info:'info' } })
2.7.6 接收路由参数
create(){ console.log(this.$route.query) console.log(this.$route.params) }
2.7.7 路由前进后退
this.$router.go(-1) // 返回上一页 this.$router.back() // 返回上一页 this.$router.go(1) // 前进一页
2.7.8 路由重定向
const router = new Router({ routes: [ {path: '/', component: index}, // 1.直接写想要跳转到的网址 {path: '/d', redirect: 'http://baidu.com',meta :{}}, // 2.根据name跳转 {path: '/e', redirect: {name: 'c'}}, // 3.定义函数 {path: '/f', redirect: (hash, params, query) => { // hash 网址信息 // params/query 接收路由参数 if (hash.query.id === 123456) { return {name: 'c'} } else { return '/a' } //最后必须return 返回一个路由路径 }}, ] })
2.7.9 全局路由钩子
判断是否登陆,有无进入页面权限。 具体参考2.7.2 路由模块化
const router = new Router({ routes: [ {path: '/', component: index}, { path: '/g', component: g, // 触发的事件 访问这个网址之后 加载模板之前 beforeEnter (to, from, next) { // to 到哪里去 // from 从哪里来 // next 是否继续往下加载模板 next() //继续往下加载模板 } } ] })
2.7.10 模版路由钩子
export default { // 模板加载之前执行 beforeRouteEnter (to, from, next) { // to 到哪里去 // from 从哪里来 // next 是否继续往下加载模板 next() //继续往下加载模板 } }
2.7.11 模版路由监听
export default { // 监听路由获取参数 watch: { '$route'(newValue, oldValue) { } } }
2.7.12 路由重置添加
router 对外提供两个方法match(负责route匹配), addRoutes(动态添加路由)。
在做路径切换transitionTo方法中,首先就会使用const route = this.router.match(location, this.current)来匹配route, 其实内部会使用matcher来做匹配。修改了matcher即新的routes生效。
对router.matcher属性做修改,即新的routes就会替换老的routes, 其实就是replaceRoutes()的含义(但是官方没有提供这个API)vueRouter.$addRoutes = (params) => { vueRouter.matcher = new Router({mode: 'history'}).matcher; vueRouter.addRoutes(params) }; vueRouter.$addRoutes([...defaultRouter, ...utilsRouter,]);
03 生命周期
3.1 周期函数
//在实例初始化之后,数据观测(data observer) 和 event/watcher 事件配置之前被调用。 beforeCreate //实例已经创建完成之后被调用。在这一步,实例已完成以下的配置:数据观测(data observer),属性和方法的运算, watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前不可见。 created //在挂载开始之前被调用:相关的 render 函数首次被调用。 beforeMount //el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。 mounted //数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。 你可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。 beforeUpdate // 路由离开当前页面 beforeRouteLeave //由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。 updated //实例销毁之前调用。在这一步,实例仍然完全可用。 beforeDestroy //Vue 实例销毁后调用 destroyed
3.2 事件对象$event
<div id='app'> <button @click='but($event)'></button> </div> <script> new Vue({ el: "#app", data: { news: [ {name: '1',age:12} ,{name: '2',age:12},{name: '3'},{name: '4'}] }, methods:{ but(event){ console.log(event) event.target.style.background = 'red'; } } }) </script>
04 组件
4.1 定义子组件
<!-- 子组件 --> <template> <p>{{ text }}</p> </template> <script> export default{ name:'child', // 组件名称 data(){ return { text:'我是子组件' } }, methods(){ // 追加文字 addText(){ this.text += '2' } } } </script>
4.2 使用子组件
<!-- 父组件 --> <template> <!-- 使用子组件 --> <child></child> </template> <script> import child from 'xxx/child.vue' // 导入子组件 export default{ methods(){}, components:{ child // 导入子组件到页面模版 } } </script>
4.3 组件传值
4.3.1 父组件传子组件
定义子组件接收参数
<!-- 子组件 --> <template> <div> <p>{{ text }}</p> <!-- 显示参数 --> <p>{{ getText }}</p> </div> </template> <script> export default{ props:{ // 接收父组件参数 getText:{ // getText 参数名称 type:String, // 参数类型 required:false,// 是否必须 default:'我是默认值' // 默认值 } }, name:'child', // 组件名称 data(){ return { text:'我是子组件' } }, methods(){ // 追加文字 addText(){ this.text += '2' } } } </script>
<!-- 父组件 --> <template> <!-- 使用子组件 --> <child :getText='我是传入的参数' ></child> </template> <script> import child from 'xxx/child.vue' // 导入子组件 export default{ methods(){}, components:{ child // 导入子组件到页面模版 } } </script>
4.3.2 子组件传父组件
<!-- 子组件 --> <template> <button @click='back'>传值</button> </template> <script> export default{ name:'child', // 组件名称 methods(){ // 传父组件值 back(){ /* 参数1:接收值的方法名称 参数2:传递的参数 */ this.$emit('backParent','我是传给父组件的值') } } } </script>
<!-- 父组件 --> <template> <!-- 使用子组件 --> <child @backParent='getVal' ></child> </template> <script> import child from 'xxx/child.vue' // 导入子组件 export default{ methods(){ // 接收子组件传值的方法 getVal(val){ console.log(val) // 我是传给父组件的值 } }, components:{ child // 导入子组件到页面模版 } } </script>
4.4 组件方法
4.4.1 父调用子方法
<!-- 子组件 --> <template> <p>{{ text }}</p> </template> <script> export default{ name:'child', // 组件名称 data(){ return { text:'我是子组件' } }, methods(){ // 追加文字 addText(){ this.text += '2' } } } </script>
<!-- 父组件 --> <template> <div> <!-- 使用子组件 ref获取子组件这个元素,定义名称为childElement --> <child ref='childElement'></child> <button @click='clickChild'>调用子组件方法</button> </div> </template> <script> import child from 'xxx/child.vue' // 导入子组件 export default{ methods(){ // 调用子组件方法 clickChild(){ // 获取子组件 let childElement = this.$refs.childElement // 调用子组件下的方法 childElement.addText() // 获取子组件下的参数值 console.log(childElement.text) // 我是子组件 } }, components:{ child // 导入子组件到页面模版 } } </script>
4.4.2 子调用父方法
<!-- 父组件 --> <template> <div> <!-- 使用子组件 --> <child ></child> <p>{{ num }}</p> </div> </template> <script> import child from 'xxx/child.vue' // 导入子组件 export default{ data(){ return { num:0 } }, methods(){ numAdd(){ num += 1 } }, components:{ child // 导入子组件到页面模版 } } </script>
<!-- 子组件 --> <template> <button @click='clickParentAdd'>调用子组件方法</button> </template> <script> export default{ name:'child', // 组件名称 methods(){ // 追加父组件值 clickParentAdd(){ console.log(this.$parent) // 获取到父组件 // 调用父组件方法 this.$parent.numAdd() } } } </script>
4.5 同级组件传值
1.定义源文件
src -sameLeve -headerTitle // 随便定义名称 // sameLeve\headerTitle.js import Vue from 'vue' export default new Vue
2.同级组件
<header/> <content/>
3.header组件传值
import headerTitle from '@/sameLeve/headerTitle' // ... methods:{ // 定义传参函数 getIframe(){ headerTitle.$emit('headerTitle',123) } }
4.content组件监听值
import headerTitle from '@/sameLeve/headerTitle' // .... mounted(){ // 初始化监听值 headerTitle.$on('headerTitle',title=> { console.log(title) // 123 }) },
4.6 高阶组件
4.6.1 刷新页面案例
App.vue
<template> <div id="app"> <router-view v-if="isRouterAlive" ></router-view> </div> </template> <script> export default { name: 'App', data () { return { isRouterAlive: true } }, provide () { //父组件中通过provide来提供变量,在子组件中通过inject来注入变量。 return { reload: this.reload } }, methods: { // 刷新页面 reload () { this.isRouterAlive = false this.$nextTick(()=>{ this.isRouterAlive = true }) } }, } </script>
其他.vue页面调用
export default { inject:['reload'], methods:{ click(){ this.reload(); // 刷新页面 } } }
4.7 动态组件
元素是vue 里面的一个内置组件
在里面使用 v-bind: is,可以实现动态组件的效果
<template> <components is='a' /> </template> <script> import a from '@/components/a' import b from '@/components/a' export default { components:{ a, b } } </script>
05 slot插槽
假如父组件需要在子组件内放一些DOM,那么这些DOM是显示、不显示、在哪个地方显示、如何显示,就是slot分发负责的活。
5.1 单个slot
<!-- 父组件 --> <template> <div> <child> <p>插入子组件的内容</p> </child> </div> </template> <script> // 引入子组件... </script>
<!-- 子组件 --> <template> <div> <p>默认显示</p> <slot></slot> <!-- 显示:<p>插入子组件的内容</p>--> </div> </template>
5.2 具名多个slot
<!-- 父组件 --> <template> <div> <child> <p slot='name1'>我是name1</p> <p slot='name2'>我是name2</p> <p slot='name3'>我是name3</p> </child> </div> </template> <script> // 引入子组件... </script>
<!-- 子组件 --> <template> <div> <p>默认显示</p> <slot name='name1'></slot> <!-- 显示:<p>我是name1</p>--> <slot name='name2'></slot> <!-- 显示:<p>我是name2</p>--> <slot name='name3'></slot> <!-- 显示:<p>我是name3</p>--> </div> </template>
06 transition过渡动画
过渡动画要在使用 if 或 show 时才能实现过渡效果
6.1 使用过渡动画
<style> /* 开始为绿颜色 */ p{ color:green; } /* .slow 为动画名+状态 */ /* 动画开始状态到正常显示的过渡, 正常状态到结束状态的过渡 */ .show-enter-active,.show-leave-active{ transition: color 3s } /* 动画初始状态,正常状态到结束状态的过渡 */ .show-enter,.show-leave-active{ color:red; } </style> <body> <div id="app"> <button @click="show = !show">按钮</button> <!-- transition 是 动画标签,嵌套的是动画的内容 --> <!-- name 是动画名 --> <transition name="show"> <p v-if="show">p标签</p> </transition> </div> <script> new Vue({ el: '#app', data: { show: true } }) </script>
6.2 与动画结合
<style> .abc-enter-active{ animation: d 3s } .abc-leave-active{ animation: d 3s } @keyframes d{ 0%{ transform:scale(0) } 50%{ transform:scale(2) } 100%{ transform:scale(1) } } </style> <body> <div id="app"> <button @click="show = !show">按钮</button> <transition name="abc"> <div v-if="show">dsadas </div> </transition> </div> <script> new Vue({ el: '#app', data: { show: true } }) </script>
07 自定义指令
7.1 全局自定义指令
<div id="app"> <div v-changeColor="{ color:'red' }" ></div> </div> <script> // 自定义组件名称changeColor Vue.directive('changeColor',(el,bind)=>{ console.log(el);// 绑定的元素 console.log(bind.value); // 传过来的数据 { color:'red' } // 标签样式变化 el.style.color = bind.value.color; }); new Vue({ el: '#app', data: {} }) </script>
7.2 模块自定义指令
<div id="app"> <div v-changeColor="'asdas'">ddd</div> </div> <script> new Vue({ el: '#app', directives: { // 自定义组件名称 changeColor changeColor: { // 当我们绑定的时候触发 只触发一次 bind(){ console.log('自定义组件绑定') }, // 主方法 inserted(el){ console.log(el); // 绑定的元素 el.style.color = 'red' }, // 绑定的数据改变的时触发 update(){}, // 组件完成一次更新的时候触发 componentUpdated(){ }, // 和元素解除绑定的时候触发 unbind(){} } } }) </script>
08 数据更新驱动
有些时候我们会遇到数据更新了,但发现视图没有更新,下面说明如何解决
8.1 对象数据
// 方案一:利用Vue.set(object,key,val) -- 全局 Vue.set(vm.obj,'k1','v1') // 方案二:利用this.$set(this.obj,key,val) --- 局部 this.$set(this.obj,'k1','v1') // 方案三:利用Object.assign({},this.obj)创建新对象 this.obj = Object.assign({}, this.obj,{'k1','v1'})
8.2 数组数据
openReaonly (item) { item.readonly = true // index item当前位置的index // 1 删除 本身 // 插入新数据,修改后的item this.conArr.splice(index, 1, item) }
09 插件模块
9.1 echart
cnpm i echarts
9.1.1 初始化
<template> <div id="rejectEchart"></div> </template> <script> import echarts from 'echarts' // 数据源 import { rejectEchartData } from '../data/rejectEchart' export default { name: 'rejectEchart', mounted(){ this.rejectEchart = this.$refs.rejectEchart }, methods: { init () { this.charts = echarts.init(this.rejectEchart) this.charts.setOption(rejectEchartData) } }, } </script>
9.1.2 标题控制
let option = { title : { text: '某站点用户访问来源', subtext: '纯属虚构', x:'center' }, // ... }
9.1.3 下载保存图片
let option = { toolbox: { feature: { saveAsImage: {} // 保存图片 } }, // ... }
9.1.4 图标显示值
let option = { series: [{ //... label:{ normal: { show: true, position: 'top' } } }] // ... }
9.1.5 图标颜色修改
legend: { // pie-实心圆 emptycircle-空闲圆 type: 'pie', // 图标 data:['临停车','月租车','免费车','储值车','军警车'], left:'center', bottom:'10%', itemWidth:10,//图例的宽度 itemHeight:10,//图例的高度 textStyle:{//图例文字的样式 color:'#ccc', fontSize:16 } },
9.1.6 折线与点颜色
series: [ { name: "温度",//鼠标放在折线点上显示的名称 type: "line",//折线图 symbol: 'circle',//折线点设置为实心点 symbolSize: 4, //折线点的大小 itemStyle: { normal: { color: "#386db3",//折线点的颜色 lineStyle: { color: "#386db3"//折线的颜色 } } }, lineStyle:{ normal:{ width:100 // 线宽 } } ]
9.1.7 xy轴颜色及宽度
xAxis: [{ gridIndex: 0,,c type: "category", data: xdata, axisLine: { lineStyle:{ color:'#272729',//x轴的颜色 width:8,//轴线的宽度 } }, }], yAxis: [ axisLine: { lineStyle:{ color:'#272729',// y轴的颜色 width:8,//y轴线的宽度 } }],
9.1.8 坐标值的颜色
axisLabel: { show: true, textStyle: { color: '#fff' } }
9.1.9 隐藏y轴线背景
yAxis: { splitLine: {show: false}, }
部分echart转载
原文链接:https://blog.csdn.net/zhumizhumi/article/details/815377659.2 获取cookie
cnpm i js-cookie
import Cookies from 'js-cookie' Cookies.set('token','token') Cookies.get('token','token')
9.3md5加密
cnpm i -S blueimp-md5
导入:import md5 from 'blueimp-md5' 普通加密:let val=md5('value');
9.4 vue-easytable表格
cnpm i vue-easytable
使用
// 引入样式 import 'vue-easytable/libs/themes-base/index.css' // 导入 table 和 分页组件 import {VTable,VPagination} from 'vue-easytable' // 注册到全局 Vue.component(VTable.name, VTable) Vue.component(VPagination.name, VPagination)
9.5 qs-url格式化
cnpm i qs
import qs from 'qs' qs.stringify(params)
9.6 二维码
cnpm i qrcodejs2
<template> <div id="qrcode"></div> </template> <script> import QRCode from 'qrcodejs2' export default { name: 'comQrcode', components:{ QRCode }, methods: { // 创建二维码 createCode ({ url, width=500, height=500, colorDark="#000", colorLight="#fff", }) { document.getElementById("qrcode").innerHTML = "" new QRCode('qrcode', { width: width, height: height, text: url, // 二维码地址 colorDark : colorDark, colorLight : colorLight, }) } }, } </script> <style scoped> </style>
10 jsx
10.1 获取ref
h是组件返回的 这个是vnode作用域实在 h所在的组件 你可以把h改成 this.$createElement
render: (h, params) => { let create = this.$createElement // 这个 return create(dataAutoComplete, { key: 'part_' + params.index, ref: 'partSelector', props: { pagename: 'part_baseselect', param: {} }, on: { 'on-select': (data) => { console.log(this.$refs) } } })
11 其他
11.1 描点
// 跳转描点 goAnchor(domId) { this.$el.querySelector(`#${domId}`).scrollIntoView() }