Vue.js作为目前最热门最具前景的前端框架之一,帮助我们快速构建并开发前端项目。 本文旨在帮助大家认识Vue.js,了解Vue.js的开发流程。
本节导航
Vue CLI
Vue CLI 是官方提供快速搭建Vue项目的脚手架工具
安装
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 个使用率非常高生命周期函数
- created 获取 $route 参数
- mounted 获取 原生DOM 和 组件实例
- beforeDestroy 销毁定时器
- activated 使用 keep-alive 时 提供的生命周期函数
- deactivated 使用 keep-alive 时 提供的生命周期函数
activated: 页面第一次进入的时候,钩子触发的顺序是created->mounted->activated
deactivated: 页面退出的时候会触发deactivated,当再次前进或者后退的时候只触发activated
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 = 2 this.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> <div class="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) // 注册多个filter Object.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.js import BaseButton from './BaseButton.vue' Vue.component('BaseButton', BaseButton)
父->子组件通讯
单向数据流,从父到子传递
父级 prop 的更新会向下流动到子组件中,子组件中不能直接修改props
// 父组件 <components :id="112233" user-id="12321312"></components> // 类型区别 // 子组件 props: { id: Number, userId: { type: String, default: '' } }
子->父组件通讯
- 一个弹窗组件,点击确定后需要重新请求父组件的列表,自定义事件
// 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.js import 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.push router.replace router.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 添加token return config }, (error) => { console.error(error) return Promise.reject(error) } ) request.interceptors.response.use( (response) => { // 判断数据是否正常返回 return response }, (error) => { // 500 return 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>
Form 表单
<el-form :model="numberValidateForm" ref="form" label-width="100px" class="demo-ruleForm"> <el-form-item label="年龄" 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号' }] } } }