从零开始学VUE

Vue
376
0
0
2022-03-27

Vue.js作为目前最热门最具前景的前端框架之一,帮助我们快速构建并开发前端项目。 本文旨在帮助大家认识Vue.js,了解Vue.js的开发流程。

本节导航

Vue CLI安装创建项目启动打包环境变量

Vue生命周期Data模板语法复杂业务组件常用库

Element安装配置栅格系统Form 表单Table 表格

Vue CLI

Vue CLI 是官方提供快速搭建Vue项目的脚手架工具

零配置开发

基于 webpack 构建

可扩展

安装

npm install -g @vue/cli

创建项目

vue create hello-world

Vue CLI v4.5.10
? Please pick a preset:233 ([Vue 2] router, vuex, dart-sass, babel, pwa, eslint, unit-mocha)  
  test ([Vue 2] router, vuex, dart-sass, babel, pwa, eslint, unit-mocha) 
> Default ([Vue 2] babel, eslint)Default (Vue 3 Preview) ([Vue 3] babel, eslint)Manually select features

选择 Vue 2 默认配置,也支持定义配置,

node_modules
public                        // 静态文件
src
	|------- assets             // 资源模块,图片等...
	|------- components         // 组件
  |------- views              // (默认没有),一般会放页面组件
	|------- App.vue            // 根组件
	|------- main.js            // 初始化 Vue 以及配置
babel.config.js               // babel 配置
package.json                  // 项目信息,npm 脚本,包版本信息

启动

cd hello-world

npm run serve

打包

npm run build

打包后的文件会放在项目根目录的dist文件。可以进入dist 启动一个 http-server 快速验证打包后的内容

环境变量

一般用来区分 开发环境 测试环境 正式环境的配置信息

.env
.env.[mode]

// .env.development
NODE_ENV=development
VUE_APP_UC=https://ucdev.meb.im/

// package.json
"scripts": {
  "serve": "vue-cli-service serve --mode development",
  "build": "vue-cli-service build",
  "lint": "vue-cli-service lint"
}

// zu'jian
console.log(process.env.VUE_APP_UC)

只有 NODE_ENV,BASE_URL 和以 VUE_APP_ 开头的变量,会被加载到 process.env. 对象中

Vue

目前正式版本是 Vue2.0,3.0还在beta版本,

生命周期

只介绍一下 5 个使用率非常高生命周期函数

  1. created 获取 $route 参数
  2. mounted 获取 原生DOM 和 组件实例
  3. beforeDestroy 销毁定时器
  4. activated 使用 keep-alive 时 提供的生命周期函数
  5. deactivated 使用 keep-alive 时 提供的生命周期函数

activated: 页面第一次进入的时候,钩子触发的顺序是created->mounted->activated

deactivated: 页面退出的时候会触发deactivated,当再次前进或者后退的时候只触发activated

完整生命周期beforeCreatecreatedbeforeMountmountedbeforeUpdateupdatedbeforeDestroydestroyed

Data

组件中 data 必须是个 函数 并且 需要return 一个对象 JavaScript 中对象是引用类型,如果data是一个对象, 一个页面组件又使用2个相同的组件, A 修改了 data 就会影响 b 的data

{
     data() {
         return {
             hello: '',
             arr: [1, 2, 3]
         }
     },
     methods: {
         updateData() {
             // 值类型更新数据this.hello = 'ni hao'
         },
         updateObj1() {
             const newObj = { ...this.obj }
             newObj.id = 2this.obj = newObj
         },
         updateObj2() {
             const newObj = Object.assign({}, this.obj, { id: 2 })
             this.obj = newObj
         },
         updateObj3() {
             // 适用于数组this.$set(this.obj, 'id', 2)
         },
         updateArr1() {
             this.arr.push(4)
         },
         updateArr2() {
             this.$set(this.obj, 3, 3)
         }
     }
 }

因为 Vue 修改了数组原型方法, 在原有方法包裹了异常, 使用直接使用数组方法,便可以会触发视图更新

push() pop() shift() unshift() splice() sort() reverse()

模板语法

Vue 的模板语法,和后端模板基本大同小异,底层其实是把模板编译成虚拟 DOM 渲染函数

{{ msg }}
 
 {{ ok ? 'YES' : 'NO' }}      // 表达式
 
 {{ todo(ok) }}      // 渲染函数的返回值
 
 <div v-html="rawHtml"></div> // 渲染原生HTML
 
 <p v-if="seen">现在你看到我了</p>
 
 <p v-show="seen">现在你看到我了</p>
 
 <ul><li v-for="item in items" >{{ item.name }}</li></ul>
 
 <divclass="static"v-bind:class="{ active: isActive, 'text-danger': hasError }"
 ></div>
 
 <div :class="[activeClass, errorClass]"></div>
<h1>{{ blogTitle }}</h1>
 
 function render(createElement) {
   return createElement('h1', this.blogTitle)
 }

复杂业务

对于比较复杂的业务逻辑,光靠模板语法的话,会把模板写得非常复杂和难以维护

<button v-if="isLogin && userType === 1 && hasLog">日志</button>
 <div v-if="logBtnVisible">日志</div>
 
 {
     computed: {
         logBtnVisible() {
             return this.isLogin && this.userType === 1 && this.hasLog
         }
     }
 }

filter

本质是一个函数,可以不用局限于filter

<span>{{isEnable| text}}</span>
 
 {
     filters: {
         text(val) {
             return val ? '激活' : '未激活'
         }
     }
 }
// main.js
 Vue.filter(key, fn)
 
 // 注册多个filterObject.keys(filters).forEach(key => {
     Vue.filter(key, filters[key])
 })

watch

<button @click="handleShowDialog">弹窗</button>
 <div v-show="visible" class="dialog"></div>
 
 {
     watch: {
         visible(val) {
             if (val) {
                 this.resetForm() // 重置表单
             }
         }
     },
     method: {
         handleShowDialog() {
             // this.resetForm()  this.visible = true
         }
     }
 }

组件

<base-button action-type="share" />
 <BaseButton actionType="share" />
 
 import BaseButton from './BaseButton.vue'
 {
     components: { BaseButton }
 }

JavaScirpt 中命名规范是 小驼峰,但是在Vue中组件的模板,命名规范推荐使用(字母全小写且必须包含一个连字符),遵循 W3C 规范中的自定义组件名,

全局注册组件

// main.jsimport BaseButton from './BaseButton.vue'
 Vue.component('BaseButton', BaseButton)

父->子组件通讯

单向数据流,从父到子传递

父级 prop 的更新会向下流动到子组件中,子组件中不能直接修改props

// 父组件
 <components :id="112233" user-id="12321312"></components>  // 类型区别
 
 // 子组件
 props: {
     id: Number,
     userId: { type: String, default: '' }
 }

String

Number

Boolean

Array

Object

Date

Function

Symbol

子->父组件通讯

  1. 一个弹窗组件,点击确定后需要重新请求父组件的列表,自定义事件
// alert.vue 组件
 <button @click="$emit('submit')">确定</button>
 
 // list.vue 列表
 <alert @submit="getList"></alert>

ref/$refs

用于活动 子组件实例 或者 原生DOM,父组件就有了控制子组件的能力

<child ref="child"></child><input ref="input"></input>
 
 {
     mounted() {
         console.log(this.$refs.child)    // 可以调用子组件所有方法this.$refs.input.focus()         // 操作 DOM
     }
 }

常用库

Vue Router

Vue Router 是 Vue.js 官方的路由管理器

// router.jsimport Vue from 'vue'import Router from 'vue-router'import Home from '../view/Home'
 
 Vue.use(Router)
 
 const router = new Router({
     routes: [
         { path: '/home', name: 'home', component: Home }
     ]
 })
 
 // App.vue
 <div id="app">
   <img alt="Vue logo" src="./assets/logo.png"><router-view></router-view></div>

路由跳转

// 组件 
 <router-link to="/foo">Go to Foo</router-link><router-link :to="{ path: 'register', query: { plan: 'private' }}">Register</router-link
 
 // 方法
 router.pushrouter.replacerouter.back

meta

权限管理,页面个性化配置,设置title

{path: 'bar',component: Bar,meta: { requiresAuth: true }}
 
 this.$route.meta

子路由

// router.js
 {
     path: '/child-View',
     name: 'childView',
   component: ChildView,
   redirect: '/child-view/child1',     // 自动重定向children: [
         { path: 'child1', component: Child1 },
         { path: 'child2', component: Child2 }
     ]
 }
 
 // ChildView
 <div>
     <h1>ChildView</h1><router-view></router-view></div>

$router/$route

route是路由信息对象,里面主要包含路由的一些基本信息,包括name、meta、path、hash、query、params、fullPath、matched、redirectedFrom

router是VueRouter的实例,包含了一些路由的跳转方法,钩子函数

axios

直接使用

axios.get('/user', {
     params: {
         id: 12345
     }
 })
 axios.post('/user', { id: 123445 })
// utils/request.js
 
 import axios from 'axios'
 
 const request = axios.create({
     baseURL: 'https://www.xxx.com/api',
     timeout: 1000,
     headers: { 'X-Custom-Header': 'foobar' }
 })request.interceptors.request.use(
     (config) => {
         // 往header 添加tokenreturn config
     },
     (error) => {
         console.error(error)
         return Promise.reject(error)
     }
 )
 request.interceptors.response.use(
 (response) => {
         // 判断数据是否正常返回return response
     },
     (error) => {
         // 500return Promise.reject(error)
     }
 )
 
 export default request
 
 // main.js// Vue 实例的原型上, 习惯加上$符号 
 Vue.prototype.$request = request
 
 // 组件this.$request.get('/aa/bb', {})
     .then(res => {
         console.log('res', res)
     })
     .catch(error => {
         console.error('error', error)
     })
 // or 同步写法(只是写法, 还是异步的)
 ;(async () => {
     try {
         const res = await this.$request.get('/aa/bb')
     } catch (e) {
         console.error('error', error)
     }
 })()


Element

安装

npm i element-ui -S

配置

全局配置,该组件可以直接使用 Element 所有组件,不必单独引入

import Vue from 'vue'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'

Vue.use(ElementUI)

局部引用

<el-button></el-button>

import { Button as ElButton } from 'element-ui'

{
	components: { ElButton  }
}

栅格系统

将页面固定分成几栏,进行页面的布局设计,使布局规范简洁有规则。

Element 分成 24 栏

<el-row :gutter="20"><el-col :span="6"><div class="grid-content bg-purple-dark"></div></el-col>
	<el-col :span="6"><div class="grid-content bg-purple-dark"></div></el-col>
	<el-col :span="6"><div class="grid-content bg-purple-dark"></div></el-col>
	<el-col :span="6"><div class="grid-content bg-purple-dark"></div></el-col>
</el-row>

从零开始学VUE

Form 表单

<el-form :model="numberValidateForm" ref="form" label-width="100px" class="demo-ruleForm">
  <el-form-itemlabel="年龄"prop="age":rules="rules"
  ><el-input type="age" v-model.number="numberValidateForm.age" autocomplete="off"></el-input></el-form-item><el-form-item><el-button type="primary" @click="submitForm('numberValidateForm')">提交</el-button><el-button @click="resetForm('numberValidateForm')">重置</el-button></el-form-item>
</el-form>
export default {
  data() {
    return {
      numberValidateForm: {
        age: ''
      },
			rules: {
				age: [
					{ required: true, message: '年龄不能为空'},
		      { type: 'number', message: '年龄必须为数字值'}
				]
			}
    };
  },
  methods: {
    submitForm(formName) {
      this.$refs.form.validate((valid) => {
        if (valid) {
          alert('submit!');
        } else {
          console.log('error submit!!');
          return false;
        }
      });
    },
    resetForm(formName) {
      this.$refs.form.resetFields();
    }
  }
}

Table 表格

<el-table :data="tableData" style="width: 100%">
  <el-table-column prop="date" label="日期" width="180"></el-table-column>
  <el-table-column prop="name" label="姓名" width="180"></el-table-column>
  <el-table-column prop="address" label="地址"> </el-table-column>
</el-table>

export default {
  data() {
    return {
      tableData: [{
        date: '2016-05-02',
        name: 'hello1',
        address: '四川成都春熙路街道xxx号'
      }, {
        date: '2016-05-04',
        name: 'hello2',
        address: '四川成都春熙路街道xxx号'
      }, {
        date: '2016-05-01',
        name: 'hello3',
        address: '四川成都春熙路街道xxx号'
      }, {
        date: '2016-05-03',
        name: 'hello4',
        address: '四川成都春熙路街道xxx号'
      }]
    }
  }
}