有你在真好 的个人博客
从零开始学VUE
阅读:2158 添加日期:2021/3/27 23:16:30 原文链接:https://www.toutiao.com/item/6921273118560420365/

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 参数
    1. mounted 获取 原生DOM 和 组件实例
    1. beforeDestroy 销毁定时器
    1. activated 使用 keep-alive 时 提供的生命周期函数
    1. 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 = 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: '' }
     }
  • 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.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>
    从零开始学VUE

    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号'
          }]
        }
      }
    }
    ICP备案号:苏ICP备14035786号-1 苏公网安备 32050502001014号