vue2项目构建

1.安装vue-cli

npm install -g @vue/cli
yarn global add @vue/cli

如需升级全局的 Vue CLI 包,请运行:

npm update -g @vue/cli
# 或者
yarn global upgrade --latest @vue/cli

2.使用vue-cli生成vue2项目

运行以下命令来创建一个新项目:

vue create vue2-demo

运行结果选择:
Default([vue 2]babel,eslint)

生成完毕之后

|-- vue2-demo
    |-- .gitignore
    |-- babel.config.js
    |-- jsconfig.json
    |-- package.json
    |-- README.md
    |-- vue.config.js
    |-- yarn.lock
    |-- public
    |   |-- favicon.ico
    |   |-- index.html
    |-- src
        |-- App.vue
        |-- main.js
        |-- assets
        |   |-- logo.png
        |-- components
            |-- HelloWorld.vue

至此vue2基础的项目生成

3.配置vue.config.js

vue.config.js文件

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
	transpileDependencies: true,
	// 保存时检查语法
	lintOnSave: false,
	// 配置别名
	configureWebpack: {
		resolve: {
			alias: {
				"src": "@"
			}
		}
	}
})

4.配置eslint

根目录新建 .eslintrc.js文件

yarn add eslint-plugin-vue eslint-plugin-html -D

编辑 .eslintrc.js

module.exports = {
  root: true,
  parserOptions: {
      sourceType: 'module'
  },
  // required to lint *.vue files
  plugins: [
    'html'
  ],
  extends: ['plugin:vue/essential', 'eslint:recommended'],
  // add your custom rules here
  'rules': {
      "vue/multi-word-component-names": 'off',   
      'space-before-function-paren': 'off',
      'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
      'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
  }
}

项目根目录新增 .eslintignore 文件,将不检查相关文件语法

vue.config.js
.eslintrc.js
babel.config.js
node_modules/
jsonconfig.json

5.配置jsconfig.json

jsconfig.json文件

{
  "compilerOptions": {
    "target": "es5",
    "module": "esnext",
    "baseUrl": "./",
    "moduleResolution": "node",
    // 跳转@路径的问题
    "paths": {
      "@/*": [
        "src/*"
      ]
    },
    "lib": [
      "esnext",
      "dom",
      "dom.iterable",
      "scripthost"
    ],
    // 解决prettier对于装饰器语法的警告
    "experimentalDecorators": true,
    // 解决.jsx文件无法快速跳转的问题
    "jsx": "preserve"
  },
  //提高 IDE 性能
  "exclude": [
    "node_modules",
    "dist",
    "build"
  ]
}

6. 引入全局事件总线

在全局main.js中的beforeCreate生命周期新增Vue.prototype.$bus = this

new Vue({
  render: h => h(App),
  beforeCreate() {
    Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
  },
}).$mount('#app')

全局事件总线的使用:

6.1.注册全局事件

使用this.$bus.$on(全局事件名称,事件回调函数)

methods(){
  callbackFn(data){
    console.log(data);
  }
},
mounted() {
  this.$bus.$on('topic1',this.callbackFn)
}

6.2.触发全局事件

使用this.$bus.$emit(全局事件名称,事件数据)

this.$bus.$emit('topic1',data)

7. 引入Vuex

安装Vuex3.0(匹配Vue2.0)

yarn add vuex@3

7.1 创建Vuex的store

在目录SRC中新建 src/store/index.js

  |-- src
    |-- main.js 
    |-- store
        |-- index.js

store/index.js中添加count的options

import Vue from 'vue';
import Vuex from "vuex";

Vue.use(Vuex);

const countOptions = {
  namespaced: true,
  state: { countNum: 0 },
  actions: {
    addCount(context, data) {
      context.commit('addCount', data);
    },
    multiCount(context, data) {
      context.commit('multiCount', data);
    },
    subCount(context, data) {
      context.commit('subCount', data);
    },
    divCount(context, data) {
      context.commit('divCount', data);
    },
  },
  mutations: {
    addCount(state, data) {
      state.countNum += data;
    },
    multiCount(state, data) {
      state.countNum *= data;
    },
    subCount(state, data) {
      state.countNum -= data;
    },
    divCount(state, data) {
      state.countNum /= data;
    },
  },
  getters: {
    countNumMul10(state) {
      return state.countNum * 10
    }
  }
}

const store = new Vuex.Store({
  modules: {
    count: countOptions
  }
})

export default store;

7.2 全局引入store

在src/main.js中引入store,加入vue的options

import Vue from 'vue'
import App from './App.vue'
import store from './store';

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
  store,  // 配置vuex store
  beforeCreate() {
    Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
  },
}).$mount('#app')

至此vuex的store全部配置完毕,在组建中新增子属性this.$store。

7.3 使用vuex

在src/components/count.vue中使用vuex

<template>
  <div>
    <div>countNum:{{ countNum }}</div>
    <div>countNum乘以10{{ countNumMul10 }}</div>
    <button @click="add(1)">add1</button>
    <button @click="multi(2)">multi2</button>
    <button @click="sub(1)">sub1</button>
    <button @click="div(2)">div2</button>
  </div>
</template>

<script>
import { mapState, mapActions, mapMutations, mapGetters } from "vuex";
export default {
  name: "Count",
  components: {},
  mixins: [],
  props: {},
  data() {
    return {};
  },
  computed: {
    ...mapState("count", ["countNum"]),
    ...mapGetters("count", ["countNumMul10"]),
  },
  watch: {},
  mounted() {},
  methods: {
    ...mapActions("count", { add: "addCount" }),
    ...mapMutations("count", { multi: "multiCount" }),
    sub(count) {
      this.$store.dispatch("count/subCount", count);
    },
    div(count) {
      this.$store.commit("count/divCount", count);
    },
  },
};
</script>
<style lang="" scoped></style>

8. 引入vue-router

8.1 安装vue-router3.0

yarn add vue-router@3

8.2 main.js引入vue-router

src/main.js

import Vue from 'vue';
import router from './router';
import VueRouter from 'vue-router';

Vue.use(VueRouter);

new Vue({  
  router 
}).$mount('#app')

8.3 编辑vue-router

8.3.1 新建基础的route

src/router/index.js


import VueRouter from 'vue-router';
import About from '@/pages/About';
import Login from '@/pages/Login';

const router = new VueRouter({
  routes: [
    {
      path: '/admin',
      name: "admin",
      component: Admin,
      meta: { loginRequired: true }
    },     
    {
      name: "login",
      path: '/login',
      component: Login
    }
  ]
})
export default router;

8.3.2 在增加admin的子路由chidren

routes: [
    {
      path: '/admin',
      name: "admin",
      component: Admin,
      // 如果需要登录才能进入的页面,需要加 meta:{loginRequired:true}
      meta: { loginRequired: true },
      children: [
        {
          name: 'home',
          path: 'home',
          component: Home,
          meta: { loginRequired: true }
        },
        {
          name: 'about',
          path: 'about',
          component: About,
          meta: { loginRequired: true },
          children: [
            {
              name: 'products',
              path: "products",
              component: Products,
              meta: { loginRequired: true },
              // 单独进入此路由时的前置守卫
              beforeEnter(to, from, next) {
                console.log(to, from, next);
                next();
              }
            },
            {
              name: 'productDetail',
              path: "productDetail",
              component: ProductDetail,
              // props通过返回函数传递
              props($route) {
                return { id: $route.query.id, name: $route.query.name }
              },
              meta: { loginRequired: true },
            },
            {
              name: 'orderDetail',
              path: "orderDetail/:id/:name",
              component: OrderDetail,
              props: true, // params映射为props传给组件
              meta: { loginRequired: true },
            },
            {
              name: 'order',
              path: "order",
              component: Order,
              meta: { loginRequired: true },
            }
          ]
        }
      ]
    },
    {
      name: "login",
      path: '/login',
      component: Login
    }
  ]

8.3.3 增加全局导航守卫


// 全局路由前置守卫,更改路由之前调用
router.beforeEach((to, from, next) => {

  console.log(to, from, next);

  if (!to.meta.loginRequired) {
    next();
  }

// 判断是否登录,如果需要登录才能进入的页面,需要加 meta:{loginRequired:true}
  if (!localStorage.getItem('userInfo')) {

    // 无登录缓存,直接跳转登录页面
    next({ name: "login" })
  }
  next();
})

// 全局路由后置守卫
router.afterEach((to, from) => {
  console.log('afterEach', to, from);
})

8.3.4 增加组件内部导航守卫

beforeRouteEnter进入路由前守卫,beforeRouteLeave离开路由前守卫

export default {
  name: "ProductDetail",
  data() {
    return {};
  },
  props: ["id", "name"],
  methods: {},
  computed: {},
  mounted() {},
  beforeRouteEnter(to, from, next) {
    console.log("beforeRouteEnter", to, from, next);
    next();
  },
  //离开守卫:通过路由规则,离开该组件时被调用
  beforeRouteLeave(to, from, next) {
    alert("暂未保存,是否要离开?");
    console.log("beforeRouteEnter", to, from, next);
    next();
  },
};

8.4 router-link跳转

路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!
通过name与params跳转

localhost:8081/#/admin/about/orderDetail/001/001-order
  <div v-for="order in orderList" :key="order.id">
    <router-link
      :to="{
        name: 'orderDetail',
        params: { id: order.id, name: order.name },
      }"
      >{{ order.name }}</router-link
    >
  </div>

通过name与query参数跳转:

localhost:8081/#/admin/about/productDetail?id=001&name=001product 
  <div v-for="product in products" :key="product.id">
    <router-link
      :to="{
        name: 'productDetail',
        query: { id: product.id, name: product.name },
      }"
      >{{ product.name }}</router-link
    >
</div>

8.5 编程式路由跳转

//$router的两个API
this.$router.push({
	name:'productDetail',
		params:{
			id:'001',
			title:'001-product'
		}
})

this.$router.replace({
	name:'productDetail',
		params:{
		  id:'001',
			title:'001-product'
		}
})
this.$router.forward() //前进
this.$router.back() //后退
this.$router.go() //可前进也可后退

8.6 路由缓存

<keep-alive include="Products"> 
    <router-view></router-view>
</keep-alive>

两个新的生命周期钩子

作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态。

具体名字:

  1. activated路由组件被激活时触发。
  2. deactivated路由组件失活时触发。

使用场景:列表页,点进详情页,缓存列表,保留用户搜索的结果与滑动的位置,提高用户体验

export default {
  name: "Products",
  data() {
    return {
      products: [
        { id: "001", name: "001product" },
        { id: "002", name: "002product" },
        { id: "003", name: "003product" },
      ],
    };
  },
  methods: {},
  computed: {},
  mounted() {},
  activated() {
    console.log("products-activated");
  },
  deactivated() {
    console.log("products-deactivated");
  },
};

9. 使用less

9.1 安装less所需依赖

# 安装less以及less-loader
yarn add less less-loader@5 -D
# 安装 style-resources-loader vue-cli-plugin-style-resources-loader
yarn add style-resources-loader vue-cli-plugin-style-resources-loader -D

9.2 更改vue.config.js的配置

首选在vue.config.js中加入less配置

const { defineConfig } = require('@vue/cli-service')
+ const path = require('path');   //引入less

module.exports = defineConfig({
	transpileDependencies: true,
	// 保存时检查语法
	lintOnSave: false,
	// 配置别名
	configureWebpack: {
		resolve: {
			alias: {
				"src": "@"
			}
		}
	},
+	pluginOptions: {
+		'style-resources-loader': {
+			preProcessor: 'less',
+			patterns: [
+					path.join(__dirname, './src/assets/styles/variables.less'),
+					path.join(__dirname, './src/assets/styles/mixins.less')
+			]
+		}
	}
})

添加src/assets/styles/variables.less文件

@red-color: #F00;

添加src/assets/styles/mixins.less文件

9.3 组件中使用less变量

比如在组件中使用@red-color变量,style的lang='less'

<style scoped lang="less">
.red {
  color: @red-color;
}
</style>
Contributors: masecho