vueJs

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总共提供了四种方式来制定环境变量:

  1. 在根目录添加.env文件,配置所有情况下都会用到的配置(不知道这个存在的意义,所有的都需要的也就不需要配置了吧)。
  2. 在根目录添加.env.local文件,配置所有情况下都会用到的配置,与.env的区别是只会在本地,该文件不会被git跟踪。
  3. 在根目录添加.env.[mode]文件,配置对应某个模式下的配置,比如:.env.development来配置开发环境的配置。
  4. 在根目录添加.env.[mode].local文件,配置对应某个模式下的配置,与.env.[mode]的区别也只是会在本地生效,该文件不会被git跟踪。

在文件中,我们只需要以key=value的方式就可以设置变量了。

//例如
FOO=bar
VUE_APP_SECRET=secret

1.3.2 使用环境变量

设置完环境变量之后就可以在我们的项目中使用这两个变量了。

不过还需要注意的是在项目的不同地方使用,限制也不一样。

  1. 在项目中,也就是src中使用环境变量的话,必须以 **VUE_APP_ **开头。例如我们可以在main.js中直接输出:console.log(process.env.VUE_APP_SECRET)
  2. 在webpack配置中使用,没什么限制,可以直接通过 process.env.XXX 来使用
1.3.3 模式

模式是Vue CLI项目中的一个重要概念。默认情况下,Vue CLI项目中有三种模式:

  1. development:在vue-cli-service serve下,即开发环境使用
  2. production:在vue-cli-service buildvue-cli-service test:e2e下,即正式环境使用
  3. 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/81537765

    9.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()
    }
    
    
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值