五. VUE2 + webpack

667 阅读19分钟

1. 基础

1.1 介绍

  • Vue.js是什么? 它是构建数据驱动的web框架
  • 为什么受欢迎?
    • 声明式渲染, 应对前后端分离
    • 渐进式框架,意味着你可以将vue作为你应用的一部分嵌入其中,带来更丰富的交互体验.
    • 快速交付,结合三方UI框架
  • Vue特点和高级功能
    • 解耦视图和数据
    • 可复用的组件
    • 前端路由技术
    • 状态管理vuex
    • 虚拟DOM
  • MVC MVP MVVM 架构模型对比
    • "MVC": Controller 薄, View 厚, 业务逻辑大都部署在View
    • "MVVM": 双向数据绑定,
    • "MVP": View 薄,不部署任何逻辑,称为被动视图(Passive View) Presenter 厚 逻辑都部署于这里.
  • 与React Angular 相似处:
    • 都使用了虚拟DOM (Virtual DOM).
    • 都提供了响应式(Reactive) 和组件化(Component)的视图组件.
    • 都将注意力集中保持在核心库, 而将其他功能 (如 路由 全局状态管理) 交给其他相关库
  • 与React Angular 不同处:
    • vue 虚拟dom 轻量
    • 学习成本低
    • vue 有 css作用域
    • React Native 能够渲染成原生的js,对应的vuex可以生成原生的js代码
  • 多页面应用
    • 缺点: 页面切换慢
    • 优点: 首屏时间快, SEO优
  • 单页面应用
    • 优点: 页面切换快
    • 缺点: 首屏时间慢, SEO差
  • 引用方式? 引入CDN,下载js文件或者 搭建脚手架
  • 如果有 template: <div>你好</div>,渲染template中的代码,如果没有template渲染el接管的div.
  • el:既可以传一个字符串也可以传一个element,如document.querySelector('')

1.2 模板语法

  • 插值:
    • 文本 {{ name }} 或用 v-text="name", 把name的值原文输出到页面,先执行{{}}花括号中的,后执行v-text. 所以同时设置的时候,后执行的v-text 会把{{}}花括号中的覆盖
    • 纯HTML <p v-html="name"> </p> 会把name变量中的值当作HTML 标签来渲染,这种转义输出 容易导致xss攻击
    • 支持表达式运算 <p>{{ 10 + 3 - 2}} </p> 页面输出11, 加减乘除,三目运算符都支持
    • v-once 当花括号中的数据{{message}}发生变化时不会被再次渲染,只会按第一次的数据内容进行展示
    • v-pre 相当于原生的pre标签, 标签里边的内容原封不动的显示出来,不会对相应的{{message}}数据进行解析渲染.
    • v-cloak 在被el接管的div中加入 v-cloak这个属性, 并且在css文件中[v-cloak] {display:none}
  • 常用指令:
    • v-bind:href="website" 绑定属性 引号里边的内筒可以写表达式 三目运算符等等
    • v-on:click="age++" 绑定事件,,其中age是属性
    • v-on:click="addfun" 其中addfun是方法,若传参需要给小括号(), 如果给小括号 括号内没给参数,函数打印的是undefined. 如果括号内给了一个参数x,打印的是该参数. 如果没给小括号,则打印的是event. 如果想手动的获得到event则 addfun(123, $event)
    • v-if增删 v-else-if, v-else, 加一个key值,防止dom复用
    • v-show显隐, v-show不能和template一起使用也不能和v-else一起使用.
  • 双向绑定
    • v-model 双向数据绑定, 动态渲染和事件监听的结合体.
    • v-model 结合radio类型使用
    • v-model 结合checkbox使用
    • v-model 结合select的使用
    • v-model 值绑定
    • v-model.lazy, 一旦有数据发生改变对应的data中的数据就会自动发生改变. 加上lazy修饰符之后data中的数据不会时时发生改变,只有在失去焦点或者回车时才会改变.
    • v-model.number 默认情况下,无论输入框输入字母还是数字,都会被当做字符串类型进行处理.加上number修饰符之后可以让输入框内容自动转成数字类型.
    • v-model.trim 加上trim修饰符可以过滤掉内容左右两边的空格

1.3 列表渲染

  • 循环: v-for
    <div  v-for="(i, index)  in  list " :key="index"> {{ i }}--{{index}}</div>
    <div  v-for="(i, index)  in  list " :key="i.id"> {{ i .text}}--{{index}}</div>
    遍历对象时 v-for="(i,key,index) in list"
    
  • 数组循环, 当修改数组内容时, 不能通过下标的形式来改变数组,
    通过数组的变异方法来修改数组才能够实现, 数据发生变化页面也跟着变,这种响应式的效果.
    • 七个: push pop shift unshift splice截取 sort排序 reserve逆序
    • 改变引用, 直接操作vm.list的引用 让它指向另外一个地址空间
    • set方法 Vue.set(vm.list, 1, 5)
    • set方法 vm.$set(vm.list, 1, 5)
  • 对象循环:
    <div v-for="i  in  objList">{{ i }} </div>
      <div v-for="(i,key)  in  objList">{{ i }}--{{key}} </div>
       <div v-for="(i,key,index)  in  objList">{{ i }}--{{key}}--{{index}} </div>
    对象和数组一样动态的用.key 赋值不会带来页面的变化,
    (1)用改引用的方式 添加数据页面更新 (好使)
    (2)set方法 Vue.set(vm.userInfo,  "address", "shenzhen")
    (3)vm.$set((vm.userInfo, "address",  "shenzhen")
    
  • v-for也可以遍历数字.
  • 补充: template 站位符, 临时标签, 可以把循环体放在这里

1.4 计算属性

  • 不想让插值语法{{}}花括号中的逻辑过多, 又想对原始数据属性进行某些格式的运算
  • 所以可以在data中进行一些重复的数据定义,但是这样有会造成冗余
  • 为此我们可以在computed中,定义计算属性,通过对原始data中的属性进行运算或者更改,就可以实现对原始数据进行格式化的操作
  • 计算属性基于它的依赖可以进行缓存
  • 计算属性只有它的相关依赖改变时才会重新计算
  • methods 依赖属性变更和非依赖属性变更, 只要数据改变 每一次都会重新计算.
  • 计算属性VS watch
    • watch中的函数是不需要调用的, 不用return, 一个属性影响多个属性时用
    • computed函数主动调用时不需加(), 需要return,多个属性影响一个属性时用, 计算属性不适用异步操作, 要用watch
  • 计算属性getter setter
    • 计算属性默认只有getter, 不过 在需要时你也可以提供一个setter
    (1) computed: {
        fullName: function() {return this.firstName + this.lastName}
    }
    (2) computed: {
        fullName: {
            get: function() {return this.firstName + this.lastName}
            set: function(value) {
                var arr = value.split(" ");
                this.firstName = arr[0]
                this.lastName = arr[1]
            }
        }
    }
    
    

1.5 动态绑定 css样式和属性和 v-on

(1)动态绑定样式: 
    第1种
        :class="{ activated: isact }"
        在css文件中,定义activated的样式内容
        在data中{ isact: true}
        methods 中this.isact = !isact
    第2种
        :class="[ ac1 , ac2, ac3.....]"
        在css文件中,定义activated1的样式内容
        在data中{ ac1: "activated1"}
        methods中 this.ac = "avtivated"
        数组里边可以嵌套对象
    第3种
        :style="{fontSize: fSize +'px', color: '#45FFDD'}" 
        data中fSize: 404种
        :style="styleobj"
        data中styleobj:{ color:"black"}
        methods中 this.styleobj.color = this.styleobj.color ==="black"? "red":"balck"5种
        :style="[ styleobj,   { fontSize:'10px' } ]"
        data中styleobj:{ color:"black"}
        methods中 this.styleobj.color = this.styleobj.color ==="black"? "red":"balck"
    
(2)动态绑定属性: 
    <div :[myname]="value"></div> 其中myname和value是变量
    如果我们希望将一个对象的所有属性,绑定到dom上,使之成为dom元素的所有属性:
    <div v-bind="myinfo"></div>  其中myinfo是变量
    <div :="myinfo"></div>  其中myinfo是变量
(3) v-on的使用
缩写 @
参数event
修饰符:
    .stop 调佣event.stopPropagation() 阻止冒泡
    .prevent 调用preventDefault() 阻止默认行为
    .capture 添加事件监听器时使用capture模式
    .self 只当事件是从监听器绑定的元素本身触发时 才触发回调
    .once 只触发一次
    .left 只当点击鼠标左键时触发
    .right 只当点击鼠标右键时触发
    .middle 只当点击鼠标中建时触发
    .passive {passive: true} 模式添加侦听器
    .native 监听组件根元素的原生事件
    .keyup.enter监听键盘按键 用.enter来监听具体哪个按键

1.6 生命周期钩子

  • 生命周期函数就是vue实例在某个时间点会自动执行的函数
  • new Vue() 创建vue实例,系统会初始化事件和生命周期函数 init Events & Lifecycle
  • beforeCreate() 组件创建之前, vue会进行外部注入 和 双向绑定相关的内容 init injection & reactivity
  • created() 组件创建后,该函数会自动执行.
(1)Has ‘el’ option ?   是否有el这个选项
有, 走yes这条线,  直接进入下一步
没有, 走no这条线,   when vm.$mount(el) is called
(2)Has ‘template’ option ?   是否有 template这个选项
yes,  Complle template into render function   用模板去渲染
no,  Complle  els  outerHTML as template  把el外层的标签当成模板
  • beforeMount() 挂载之前, 模板和数据相结合的DOM元素,挂载到页面上

  • mounted() 挂载完毕,自动执行该函数

  • beforeUpdate() 在挂载的情况下, 当数据改变 还没重新渲染之前,该函数自动执行

  • Updated 重新渲染之后, 该函数自动执行

  • beforeDestroy() 在组件销毁之前会调用 vm.$destroy()

  • Destroyed() 在组件销毁之后会调用

  • activated

  • deactivated

  • errorCaptured

1.7 相关细节

  • 可以实例化多个vue实例, 分别接管不同的div 标签
  • tr封装成子组件, table中是tbody,tbody中是tr. 页面用子组件名调用子组件,会出现结构上的错误. 将<row></row> 改写成<tr is="row"> </tr>. 同样ul,ol,select这样的标签也是一样的
  • 子组件的data 定义时必须是一个函数,不能像根组件一样是一个对象. 同时这个函数return一个对象,这个对象中包含相应的数据. 之所以这样是因为不同的地方调用这个子组件的时候,会有自己不同的数据.而根组件转只会被调用一次,子组件会被调用多次.
  • 属性ref="name1" 是用来标记标签的,当使用时 this.$refs.name1 获得当前Dom节点

1.8 自定义指令

(1) 新建一个js文件
export default {
    bind(el, binding){   // 钩子函数, 指令绑定
        
    },
    inserted(el, binding) {   // 钩子函数, dom插入到父级元素
        const roles = store.state.userInfo.roles
        if(!roles.includes(binding.value)) {
            el.parentNode.removeChild(el)
        }
    }
}
(2)
在使用的文件.vueimport hasRole from './xxx/role.js'
directives: {
    hasRole
}

<button v-hasRole="'admin'"> </button>




2.组件

2.1 基础

  • 组件定义和使用
(1) 创建组件构造器对象
    const navC = Vue.extend({
        template:'
        <div>
            <h2>标题</2>
            <p> 这是内容</p>
            <button>按钮</button>
        </div>
        '
    })
(2) 注册组件
Vue.component(' 使用时的标签名 my-nav',' 组件构造器 navC ')
(3) 使用组件:  在html中 <my-nav> </my-nav> 来使用这个组件
  • 全局组件
(1) Vue.component('标签名','组件构造器')
全局组件可以在多个vue实例中使用

(2)vue2.x 版本注册全局组件
    Vue.component('标签名','{
        template:'
        <div>
            <h2>标题</2>
            <p> 这是内容</p>
            <button>按钮</button>
        </div>
        '
    }')
    这种方式,内部已经帮你extend()
  • 局部组件
(1) const navC = Vue.extend({template: ...........})
在vue实例中用components: { nav-c: navC }
(2 )vue2.x 版本注册局部组件
    const navC = {
        template:'
        <div>
            <h2>标题</2>
            <p> 这是内容</p>
            <button>按钮</button>
        </div>
        '
    }
    在vue实例中用components: { nav-c: navC }

2.2 组件模板抽离

<template id=nav3>
    <div>
        <h2>标题</2>
        <p> 这是内容</p>
        <button>按钮</button>
    </div>
</template>
或者是如下写法
<script type="text/x-template" id="nav3">
    <div>
        <h2>标题</2>
        <p> 这是内容</p>
        <button>按钮</button>
    </div>
</script>


最后注册组件:
Vue.component('nav', {
    template: '#nav3'
})

2.3 父组件给子组件传数据

  • 页面通过绑定属性的方式向子组件传值,子组件中用props接收.

  • 绑定时不支持驼峰命名

  • 不要在子组件中修改父组件传来的数据,保证单向数据流.因为当页面传过来的数据不是原始值,是引用值的时候,子组件修改props中引用类型的数据,也会影响到其他子组件数据的值.

  • 子组件对页面传来的数据,进行参数校验.

(1)props: { content: String } 此时页面要是传来的不是字符串类型就会报错.
(2)props: { content: [String,Number] }
(3)props: { content: {
    type: String,
    required: true,
    default: '这是默认值'
}}
(4)props: {
    type: String,
    validator: function(value) {
        return (value.length > 5)
    }
}
(5)非props特性,父组件向子组件传递了一个数据,而子组件并没有接收这个数据,这时候在子组件使用这个数据就会报错.
  父组件向子组件传递一个数据,若子组件接收了,这个属性就不会出现在子组件中.若是子组件没有接收,这个属性就会出现在子组件的属性当中.

2.4 父组件给子组件传css

2.5 父组件给子组件传 html结构标签

  • 页面中给子组件传递标签,子组件template中用slot来接收, 如果父组件不传,子组件的slot可以有自己的默认值.
  • 不同的位置插入不同的内容,可以使用具名插槽slot="theName"
  • 作用域插槽??? 页面使用插槽给子组件传标签,但是内容由子组件来决定.

2.6 子组件给父组件传事件

  • 子组件methods内事件函数中 this.$emit('click') 向外触发事件,父组件中@click="handleChange",来接收这个事件.
  • 如果父组件中@click.native="handleClick", 这样接收的就是原生的事件.

2.7 非父子组件的传值

  • 利用bus总线机制解决非父子组件传值 ???
Vue.prototype.bus = new Vue()
子组件的事件处理函数中 this.bus.$emit('change', this.content)
在子组件mounted中 this.bus.$on('change',function(value) {  })

2.8 父子组件的相互访问

  • 父组件访问子组件: 使用.children相当于通过数组下标的形式获得子组件,.children 相当于通过数组下标的形式获得子组件, .refs 相当于用对象key值的形式获得子组件.
  • 子组件访问父组件: 使用.$parent

2.9 动态组件和v-noce

(1) <component :is="type"> </component> 他会根据is中数据的变化,自动加载不同的组件.这就是动态组件
(2) 以上在组件的切换过程中,会销毁原来的组件,重新创建一个新的组件. 这样会耗费性能
(3)在子组件的template中,根div标签加v-once属性,当子组件被切换显示的时候,会直接的从内存中读取,提高性能

3. 组件动画

3.1 transition动画

<transition name="fade3"> <div>需要添加动画的标签</div> </transition>

  • .fade3-enter ???
  • .fade3-enter-active ???
  • .fade3-enter-to ???
  • .fade3-leave ???
  • .fade3-leave-active ???
  • .fade3-leave-to ???

3.2 Animate.css库

  • ???

4. 模块化开发

4.1 闭包和模块

  • 匿名函数(闭包)虽然解决了之前多个js文件命名冲突的问题.但是代码复用率低
  • 所以我们可以把将要暴露到外边的变量,使用一个模块 导出去.
  • 不同的模块的可以单独封装自己的属性和方法.
  • 这就是模块最基础的封装,事实上模块的封装还有很多高级的话题
  • 常见的模块化规范:CommonJS, AMD, CMD, 也有ES6的Modules

4.2 CommonJS

  • 模块化有两个核心: 导出和导入, webpack用的是Commonjs
(1) CommonJS的导出
    flag: true,
    var name = 'lisi'
    var age = 18
    test(a,b) {
        return a+b
    },
    demo(a,b) {
        return a*b
    }
module.exports = {
    test,
    demo,
    name,
    age
}
(2) CommonJS的导入
let { test,age,name } = require('./moduleA.js')
// 等同于
let modA = require('moduleA')
let test = modA.test
let age = modA.age
let name = modA.name 

4.3 Es6新增两个关键字 export(导出)/import(导入)

  • 导出方式一: export { name,age }

  • 导出方式二: export var num=30;

  • export 除了导出变量和对象, 也可以导出函数 和导出类,

  • 导入步骤一: 在HTML代码中引入两个js文件 <script src="aaK.js" type='module'> </script>

  • 导入步骤二: import { name,age } from "./aaK.js"

  • export default xxx :某些情况下,一个模块中包含某个功能,我们并不希望给这个功能命名, 而且让导入者自己来命名. 这时用export default来导出模块里边想传出来的内容.

  • 在同一模块中 不允许同时纯在多个export default xxx

  • import * as a9 from "aak.js", 使用时a9.name a9.age

5. webpack

5.1 认识

  • 从本质上来讲,webpack是一个现代的JavaScript应用的静态模块打包工具
  • grunt/gulp也可以用来打包, grunt基本没人用了. 对于gulp,我们可以配置一系列的task任务流,并且定义task要处理的事务(例如es6,ts转化,图片压缩,sass转成css),之后让grunt/gulp来一次执行这些task,而且让整个流程自动化. 所以grunt/gulp也被称为前端自动化任务管理工具.
  • 什么时候用grunt/gulp?
    • 如果你的工程模块依赖非常简单,甚至是没有用到模块化的概念.
    • 只需要进行简单的合并,压缩,就使用grunt/gulp 即可
    • 但是如果整个项目使用了模块化管理,而且相互依赖非常强,我们就可以使用更加强大的webpack了
  • 区别: grunt/gulp更加强调的是前端流程的自动化,模块化不是它的核心. webpack更加强调模块化开发管理,而文件压缩合并,预处理等功能是他附带的功能. webpack各种模块化都支持(commonJS,es6等等)所以它的功能更加强大.

5.2 webpack安装

  • webpack依赖于node环境,node环境为了可以正常执行必须依赖很多包. 所以要安装一个工具 npm工具(node packages manager). npm这个工具只是为了 帮助我们管理node环境下边的各种包而已.
  • 全局安装: npm install webpack -g
  • 局部安装: npm install webpack --save-dev 是开发时的依赖, 项目打包后不需要继续使用的依赖. 所以不用打包到包里. 运行依赖,是项目能正常运行的依赖,需要打包到包里
  • package.json 这里边定义一些脚本script, 这个脚本里用的就可以是局部的

5.3 webpack的使用

  • src文件夹,放的是源码
  • dist文件夹,放的是打包之后的内容. [distribution发布]
  • 打包: webpack ./src/main.js ./dist/bundle.js
  • 在页面引入 bundle.js

5.4 webpack的配置

  • 不用每一次改变发生更新都打包一次webpack ./src/main.js ./dist/bundle.js 我们可以在webpack.config.js文件里边进行配置:
(1) 在node里获取动态路径, 首先npm init 来初始化,回车之后, 起包名,写版本号--->会生成package.json
(2) npm install 如果package中有依赖的东西,会安装一些相应的依赖

const path = require('path')  //获取动态路径
module.exports = { 
entry: './src/main.js',  
output: {
        // resolve是node封装的内置函数,__dirname也是node里边的用于获得绝对对路径
        path: path.resolve(__dirname,'dist')
        filename: 'bundle.js'
    }
}
  • 通过commonjs方式导出, 在终端执行 webpack就可以了.
  • 让webpack根 npm run build映射起来,在package.json中 scripts:{'build': "webpack"}

5.5 loader

  • loader是webpack中一个非常核心的概念,在main.js中 require('xx.css')
  • webpack主要来处理我们写的js代码,并且webpack会自动处理js之间相关的依赖. 但是在开发中我们不仅仅有基本的js代码处理,我们也需要加载css 图片 也包括一些高级的将ES6转成ES5代码,将TypeScript转成ES5代码,将scss less转成css,将.jsx .vue文件转成js文件等等.
  • 对于webpack本身的能力来说,这些转化是不支持的.
  • 所以我们给webpack扩展对应的loader就可以啦.
  • 使用过程
  • npm install --save-dev css-loader
  • webpack.config.js中
	module关键字下rules: [test: /\.css$/ , use:['css-loader']
	图片用url-loader    当图片大小大于limit时用 file-loader,在webpack.config.js文件中,output: {publicPath: 'dist/'}, publicPath这个属性的目的就是在url前边加上一个对应的路径

5.6 ES6转成ES5

  • 安装 npm install --save-dev babel-loader@7 babel-core babel-preset-es2015
  • 在webpack.config.js中module关键字下rules: 配置

6. 在webpack里边配置Vue

6.1 安装vue

  • 运行时依赖 npm install vue --save
  • main.js中 import Vue from 'vue'
打包vue时有 runtime-only和runtime-compiler两个版本
runtime-only 代码中 不可以有任何的template
runtime-compiler 代码中可以有template, 因为有compiler可以用于编译template
解决这个问题,在webpack.config.js中
resolve: {
    extension: ['.js','.css','.vue'],   // 省略扩展名
    alias: {  // 别名
        'vue$': 'vue/dist/vue.esm.js'
    }
}
  • 单页面复应用SPA ( simple page web application )
  • el和template之间的关系
  • 安装:npm install vue-loader vue-template-compiler save-dev

6.2 plugin

  • plugin是插件的意思, 通常对现有的框架进行扩充扩展的.
  • webpack中的插件,就是对webpack现有的功能进行扩充,比如打包优化,文件压缩等等.
  • loader和plugin的区别:
    • loader主要用于转换某些类型的模块,它是一个转换器.
    • plugin是插件,它是对webpack本身的扩展
  • plugin的安装
    • npm install plugin
    • 在webpack.config.js中配置plugins
  • 哪些插件可以扩充webpack:
1.添加版权的plugin
    (1)我们先来使用一个最简单的插件,为打包的文件添加版权声明,这个是webpack自带的插件.
    (2)在webpack.config.js文件中
    const path = require('path')
    const webpack = require('webpack')
    module.exports = {
        plugins: [
            new webpack.BannerPlugin('最终版权归')
        ]
    }
2. 打包html的插件
    (1)安装: npm install html-webpack-plugin --save-dev
    (2)配置webpack.config.js 中
    const HtmWebpackPlugin = require('html-webpack-plugin')
    plugins: [
        new webpack.BannerPlugin('最终版权归'),
        new HtmlWebpackPlugin({
            template: 'index.html'
        })
    ]
3.对js进行压缩的插件
    (1)安装: npm install uglifyjs-webpack-plugin@1.1.1 --save-dev
    (2)配置webpack.config.js 中
    const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')
    plugins: [
        new webpack.BannerPlugin('最终版权归'),
        new HtmlWebpackPlugin({
            template: 'index.html'
        })
        
    ]

6.3 搭建本地服务器

  • 搭建本地开发服务器,这个服务器是基于node.js的,内部使用express框架,可以实现我们想要的让浏览器自动更新显示我们修改后的结果.
  • 不过它是一个单独的模块,在webpack中使用之前需要先安装它.
  • 把我们未来要打包生成的东西,先暂时放到内存里边, 以后npm run build可能只需要执行一次就可以了.输入npm run build才会将dish文件存放到磁盘里边.
  • npm install --save-dev webpack-dev-server@2.9.1 提供一个开发时的服务的.
  • 在webpack.config.js下配置
devServer: { 
    contentBase: '/dist',   //要服务的文件夹
    inline: true,    // 是否需要实时监听
    port: 默认是8080
    historyApiFallback: 在SPA页面中,依赖H5的history模式
} 
package.json文件中scripts下 'dev': "webpack-dev-server  --open"
终端: npm run dev 来运行这个本地服务器

6.4 webpack.config.js文件分离

  • 新建base.config.js文件, 公共的东西放到这里
  • 新建dev.config.js文件, 开发依赖放到这里
  • 新建prod.config.js文件, 生产的东西放到这里
  • 把webpack.config.js文件的内容 按类别复制到以上三个文件里边
  • 开发编译时只需要base.config.js和dev.config.js, 打包时只需要base.config.js和prod.config.js
  • 安装合并包: npm install webpack-merge --save-dev
  • 在prod.config.js文件中:
const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')
const webpackMerge = require('webpack-merge')
const baseConfig = require('./base.config')
module.exports = webpackMerge(baseConfig,{
    plugins:  [
        new UglifyjsWebpackPlugin()
    ]
})
在package.json文件中: 
    "build": "webpack --config ./build/prod.config.js"
    "dev": "webpack-dev-server --open --config ./build/dev.config.js"
在base.config.js文件中, output: { path: resolve(__dirname, '../dist') }

6.5 安装vue脚手架CLI

  • CLI是 Command-Line Interface,翻译为命令行界面,俗称脚手架
  • 安装脚手架( 基于nodejs环境和webpack )
    • npm install -g @vue/cli
    • yarn global add @vue/cli
    • 卸载 npm uninstall -g @vue/cli
    • 卸载 yarn global remove @vue/cli
    • 更新 npm i -g @vue/cli
    • vue --version 来检测是否安装成功
    • 也可以先安装淘宝镜像,然后用cnpm代替npm来进行模块的安装: npm install -g cnpm --registry=https:/registry.npm.taobao.org
    • 配置淘宝镜像: npm config set registry registry.npm.taobao.org
  • runtime-compiler和runtime-only的区别
new Vue({
    el: "#app",
    template: '<App/>',
    components: {App}
})

new Vue({
    el: '#app',
    render: h => h(App)
})
runtime-compiler: template 解析成 ast(abstractTree) 编译成 render 生成 Vdom 渲染成 UI
runtime-only: render 生成 Vdom 渲染成 UI [性能更高,]

h是形参,实参是函数createElement
createElement('标签',{标签的属性},['标签的内容'])
例子: return createElement('h2',{class: 'box'},['你好' , 'hello'])
  • 脚手架3 创建项目: vue create vue-project
    (1)
    创建项目的 各个选项:
    *Babel 
    TypeScript
    *Progressive Web App  (PWA) Support 先进: 缓存很多东西,推送通知
    *Router
    *Vuex
    *CSS pre-processors
    Linter / Formatter
    Unit Testing
    E2E Testing
    Use class-style component syntax? (Y/n) 是否使用class风格的组语法
    (2)
    *In dedicated config files
    In package.json
    (3)
    
  • 运行项目
    • cd到 项目目录下
    • npm init 和 npm install
    • npm run serve
    • 打包 npm run build

7.vue-router的相关技术

路由: 就是通过互联的网络把信息从原地址传输到目的地址的活动

7.1 第一阶段

  • 后端路由: 后端处理URL和页面之间的映射关系
  • 后端渲染: 后端通过jsp等技术,从数据库读取数据,并且将它动态的放在页面中.服务器传到客户端的是html和css

7.2 第二阶段

  • 前后端分离阶段: 后端只负责数据,不负责任何界面的内容. 前端通过Ajax获取数据. 目前很多网站依然采用这种模式
  • 前端渲染: 浏览器中显示的网页大部分内容都是由前端写的js代码在浏览器中执行, 最终渲染出来的网页.

    7.3 第三阶段

  • 单页面富应用阶段: SPA最主要的特点就是在前后端分离的基础上加了一层前端路由.

7.4 vue-router前端路由

  • 前端路由的核心是什么? 在改变URL时, 页面不进行整体的刷新. 怎么实现这个功能?
方法一: 改变url上边的哈希值, location.hash = 'foo'
        如果改变href会刷新, location.href = 'aaa'
方法二: 通过history的改变, history.pushState({},'',home)
	存储路由 路径的是一个栈结构,先进后出,浏览器显示的路径是栈顶, 也就是最后一个被压入栈的路径.
    history.back() 执行这个移除原来的栈顶,第二个成为新的栈顶,浏览器路径会展示这个新的栈顶   back() 方法相当于go(-1)   forward()相当于go(1)
    history.replaceState({},'',home) 这个是替换当前路径,是不能返回的

7.5 安装和使用vue-router

  • npm install vue-router --save
  • 在模块化中使用
    • 导入VueRouter
    • Vue.use(VueRouter)
    • 创建路由实例对象,const router = new VueRouter({ routes })
    • 导出对象
    • main.js引入并且挂载
  • router-link相当于a标签, router-view是站位的, keep-alive是从缓存区取数据.
  • redirect 重定向, 设置默认路径

7.6 router-link的属性值

  • to属性 相当于 a标签的href
  • 不想默认渲染成a标签了, 通过tag="button"属性来设置,
  • replace 跳转不可返回
  • 在css中通过 .router-link-active 给活跃的router-link设置单独的样式. 若要自己改变这个类名:
    • (1)可通过active-class='active2'属性来 指定自定义的类名
    • (2)在router目录下index.js文件中, 创建路由对象时添加linkActiveClass: 'active2'
  • 不用router-link组件来渲染, 通过代码修改路由的路径
this.$router.push('/home')
this.$router.replace('/home')

7.7 动态路由

(1) 在router.js 文件中
{ 
    path: '/user/:userid',
    component: User
}
(1) 首页 (--->跳用户页)
<router-link :to=" '/user/' + userid "></router-link>
data() {
    return {
        userid:'123'
    }
}

(2) 用户页
computed: {   // 当前哪个路由处于活跃状态,this.$route就是哪个对象.
    userId() {
            return this.$route.params.userid
    }
}

7.8 路由懒加载

  • component: () => import('components/Home')

7.9 路由嵌套

  • children: 该数组中的内容各式 与routes一样即可

7.10 路由传参数

传参两种方式

  • query的方式: 大量的数据用query
(1) 组件跳转
<router-link :to="{path: '/user', query: {name: 'zhang', age: 19, height: 170} }"> 
    用户 
</router-link>
(2) 代码跳转
methods中  this.$router.push('/user/' + this.userid ) 改成对象写法
this.$router.push({
    path: '/user',
    query: {
    	name: 'zhang',
        age: 19
    }
})

目标页面取值{{$route.query.name}}

7.11 routerrouter与 route的区别

  • $router 是new出来的路由对象
  • $route 是当前正在访问 处于活跃状态的那个路由

7.12 导航守卫

(1)全局守卫
// 前置守卫
router.beforeEach((to, from, next) => {
    document.title = to.matched[0].meta.title   // 有路由嵌套要用matched
    // 手动调
    next()
})

// 后置守卫
router.afterEach((to, from) => {
})

(2)路由独享守卫
routes: [
    {
        path: '/user',
        component: User,
        beforeEnter:(to, from, next) => {
            // 进入到这个路由就会调用
        }
    },
    {},
    ............
]
(3)组件内守卫
// 进入之前
beforeRouteEnter(to, from, next) {
    // 在组件渲染前调用, 里边不能用this
}
// 路由改变,但改组件被复用
beforeRouteUpdate (to, from, next) {
    // 例如 /user/:id,  在/user/1 和 /user/2之间进行跳转时触发,, 可以用this
}
beforeRouteLeave (to, from, next) {
    // 离开之前调用, 可以用this
}

7.13 keep-alive

  • router-view 是一个组件, 如果直接用keep-alive,所有路径匹配到的视图组件都会被缓存
  • 用了keep-alive包裹的组件,页面切换时, destroy钩子将不会触发, 因为改组件没有销毁
  • 但是多了两个勾子, activated 和 deactivated 在活跃和不活跃之前切换
  • include 只有匹配的组件会被缓存
  • exclude 任何匹配的组件都不会被缓存

8. promise

promise是异步编程的一种解决方案

8.1

  • new Promise(参数), 参数本书就是一个函数
  • new Promise( (resolve, reject) => { resolve() 一旦你在这个位置调用resolve,他就会在最后边调用一个.then()})

8.2 promise三种状态

  • 首先当我们开发中有异步操作时, 就可以给异步操作包装一个Promise

  • pending: 等待状态,正在进行网络请求或者定时器没到时间等等

  • fulfill: 满足状态,当我们主动回调了resolve时,就处于该状态,并且会回调.then()

  • reject: 拒绝状态,当我们主动回调了reject时,就处于该状态,并且会回调.catch()

  • .then(函数1,函数2) 函数2是当reject时,自动执行函数2

  • .then(函数1).catch(函数2) 这样也可以执行函数2

  • then中 可以继续返回一个resolve

(1)
new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('aaa')
    }, 1000)
}).then(res => {
    // 1.这里有100行自己的业务代码
    // 2.对结果进行处理
    return new Promise((resolve, reject) =>{
        resolve(res + "567")
    })
})

(2)
上边的第2步可以简写成 return Promise.resolve(res + '567')
也可以简写成 return res + '567'

链式调用过程中, 各个.then中出现了异常,都会在.catch中捕获到,  手动抛出的异常throw也能在catch中捕获到
(3)
Promise.all([网络请求1, 网络请求2]).then(results => {
    results是一个数组,, 里边包含了第一个请求的结果,, 第二个请求的结果
})

9. Vuex

9.1 Vuex是做什么的

  • Vuex是一个专为Vue.js应用 开发的状态管理模式.
  • 它采用 集中式存储管理 应用的所有组件的状态, 并以相应的规则保证状态以一种可预测的方式发生变化.
  • Vuex 也集成到Vue的官方调试工具devtools extension,提供了诸如零配置的time-travel调试,状态快照导入导出等高级调试功能.

9.2 状态管理是什么

  • 状态管理模式,集中式存储管理: 非父子组件公共的状态值,就是公共变量 全部存储在一个对象里. 多个组件可以共享这个对象里边的变量.
  • 这个对象可以自己封装,但是自己封装做不到一处变处处变的响应式.
  • 官方已经封装好了 这个对象,就是Vuex

9.3 什么情况需要用到状态管理

  • 用户登录状态, 用户名称,头像,地理位置
  • 商品的收藏,购物车的物品

9.4 vuex单界面状态管理到多界面状态管理

  • 单页面状态管理 单页面状态管理
  • 多页面状态管理 多界面状态管理
(1) 不要直接修改state里边的值, 直接改造成浏览器插件Devtools监听不到
(2) 要通过mutations来进行修改
(3) 若有一步操作则 通过Actions再通过mutations来修改

9.5 vuex中state

  • 用来定义公共的状态值
  • 在组件中用 $store.state.age 来取值
  • vuex提出使用单一状态树,什么是单一状态树? Single Source of Truth 可以翻译成单一数据源.

9.6 Getters 相当于组件中的计算属性

  • 在getters中,自定义一个方法对state中数据进行一些格式化处理等等操作.
  • 在组件中 $store.getters.myPower()
  • gettes中的第一个参数默认是state,第二个参数是getters本身
  • getters中自定义的方法,可以返回一个函数, 使用处自己给函数传参数

9.7 vuex中mutations

(1)
第一个参数默认是state,第二个参数是使用出传过来的参数
在mutations中自定义方法
increment(state) {state.age++}
increment(state) {state.age--}
(2)
在使用的组件中: 普通封装 this.$store.commit('increment', name)  来改变值
在使用的组件中: 特殊封装 this.$store.commit({
	type: 'increment',
    name: name
}) 来改变值
(3)vuex响应式原理??
(4)类型常量??

9.8 Action 有异步操作放在这里

  • action类似于mutations, 但是它可以代替mutation来进行异步操作
  • action默认的第一个参数是context, context相当于store
  • context.commit('increment',name) 其中increment是定义在mutations中的方法
  • 在使用的组件中 this.$store.dispatch('update') 其中update是自定义在actions中的方法
  • 传参??

9.9 module模块化??

9.10 store的目录组织??

10. 网络请求(axios)

10.1 Vue中发送网络请求方式非常多

  • 选择一: 传统的ajax是基于XMLHttpRequest(XHR)
  • 选择二: jQuery-Ajax
  • 选择三: jsonp
  • 选择四: axios

10.2 axios

  • 功能特点
    • 在浏览器中发送XMLHttpRequests请求
    • 可以在node.js中发送http请求
    • 支持PromiseApi
    • 请求拦截和响应
    • 转换请求和响应数据等等
  • 支持多种请求方式
	axios(config)
	axios.request(config)
	axios.get(config)
	axios.delete(url [,config])
	axios.head(url [,config])
    
	axios.post(url [,data[,config]])
	axios.put(url [,data[,config]])
	axios.patch(url [,data[,config]])
  • axios请求细节
(1)get请求
	get 请求数据用 params: {} 传参数
(2)并发请求
	axios.all([ axios(), axios() ]).then(results=>{
    	conlsole.log(results)
    })
    
    axios.all([ axios(), axios() ]).then(axios.spread((res1, res2=>{
    	console.log(res1)
        console.log(res2)
    }))
    
    all方法是所有请求成功之后在往下进行
    使用axios.spread可以将返回回来的数组结果 进行展开
    
  • axios 全局配置
axios.defaults.baseURL = "123.123.123.23.:8000"
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'
常见的配置项 如下: 
method    请求方式
baseURL: 'http://www.xxx.com/api'请求根路径
transformRequest: [function(data) {}] 请求前的数据处理 (生命周期函数)
transformResponse: [function(data) {}] 请求后的数据处理 (生命周期函数)
headers: {'x-Requested-With': 'XMLHttpRequest'},
params: {id:12}   get查询参数对象
data: {key: 'aa'}   post 传参数的请求体
paramsSerializer: function(params){ } 查询对象序列化函数
timeout: 1000,   超时时间
withCredentials: false     跨域是否带Token
adapter: function(resolve,reject,config){}    自定义请求处理
auth: {uname: '', pwd: '12'}  身份验证信息
responseType: 'json'   响应后的数据格式

10.3 axios实例

  • 如果不封装, 默认使用全局的axios 和 全局的配置发送网络请求
  • const instance1 = axios.create( {} ) 来创建实例1
  • const instance2 = axios.create( {} ) 来创建实例2
import axios from 'axios'
import https from 'https'

export function myaxios(config) {
  // 1 创建axios的实例
  const instance = axios.create({
    // baseURL: urlDns,
    timeout: 5000,
    // httpsAgent: new https.Agent({   // 取消验证ssl证书
    //   rejectUnauthorized: false
    // }),
    headers: {
      'Content-Type':'application/x-www-form-urlencoded',   // 设置传输的数据格式text/html   application/x-www-form-urlencoded   application/json
    }

  })

  // 2.1 axios的拦截器,请求时的拦截 (发请求之前的 生命周期函数)
  instance.interceptors.request.use(config => {
    console.log("请求成功时: 触发的拦截")
    // 1. 比如config中的一些信息不符合服务器的要求
    // 2. 比如每次发送网络请求时, 都希望在界面中显示出一个请求的图标
    // 3. 某些网络请求,必须携带一些特殊的信息. (如登录要token 否则让他跳到登录页面)
    return config
  },err => {
    console.log("请求失败失败时: 触发的拦截")
    console.log(err)
  })

  // 2.2 响应时的拦截  (数据返回之前的 生命周期函数)
  instance.interceptors.response.use(res => {
    console.log("响应成功时: 触发的拦截")
    console.log(res)
    return res
  }, err => {
    console.log("响应失败时: 触发的拦截")
    console.log(err)
  })

  // 3 发送真正的网络请求
  return instance(config)   // 返回是一个promise对象
}

12.插件

12.1 console插件

  • npm install babel-plugin-transform-remove-console --save-dev
  • 在babel.config.js中 plugins数组中 增加'transform-remove-console'
  • process.env.NODE_ENV === 'production' 这是生产环境

element-UI (PC端框架)

Vant (移动端框架)