精华内容
下载资源
问答
  • 本文档主要是本人对Vue和Ant Design Vue学习后总结的经验,方便其它同事能够通过该文档对该技术有所了解 , 同时方便产品团队对前后分离的技术有增加知识储备。 目录如下 2.Vue经验分享 2.1 Vue介绍 2.2 Vue的MVVM...
  • vue学习文档.pdf

    2019-12-10 17:17:50
    vue学习文档,比较全面。 学习
  • Vue.js权威指南高清版PDF,非常经典的文档,供大家学习下载。
  • vue学习视频.txt

    2018-01-03 12:12:49
    vue学习视频vue学习视频vue学习视频vue学习视频vue学习视频vue学习视频vue学习视频vue学习视频vue学习视频vue学习视频vue学习视频vue学习视频vue学习视频vue学习视频vue学习视频vue学习视频
  • Vue学习总结

    千次阅读 2019-08-07 10:52:15
    Vue 基础 指令: 插值表达式: 语法:{{要插入的值}},此时相当于占位符,到时候{{关键字}}会被替换成 Model中的数据 bug点: 当网速较慢时,Vue初始化未完成时,插值表达式不能被解析,会直接显示 出来,加载完毕...

    Vue 基础

    指令:

    插值表达式:
    语法:{{要插入的值}},此时相当于占位符,到时候{{关键字}}会被替换成
    Model中的数据
    bug点:
    当网速较慢时,Vue初始化未完成时,插值表达式不能被解析,会直接显示
    出来,加载完毕之后又会被替换成真实结果,造成闪烁(Vue的加载代码写在
    HTML元素之后时会出现该bug)
    1.v-cloak指令:
    语法:把它当作HTML元素的一个属性使用
    示例:

    {{msg1}}


    特点:
    当Vue加载完毕后,v-cloak属性会自动消失
    使用:
    添加如下CSS样式:让所有带v-cloak属性的元素隐藏,Vue加载完成后显示
    实现,解决插值表达式闪烁的问题
    [v-cloak]{
    display: none;
    }

    2.v-text:
    语法:


    作用:和插值表达式一样,用于给HTML元素添加文本
    区别:
    1.v-text不会存在闪烁问题
    2.v-text会将Model中的数据完全替换到HTML元素中(覆盖)
    3.插值表达式只会将Model中的数据替换到插值表达式中(占位符内容的替换)
    相同点:
    1.插值表达式和v-text都可以给HTML元素显示文本
    2.插值表达式和v-text都不会解析要添加文本中的HTML标签(原样显示)

    3.v-html:
    和v-text的唯一区别:给HTML添加内容时会解析内容中的HTML

    代码测试:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>基础表达式</title>
    </head>
    <body>
    <h1>v-cloak、v-text、v-html指令以及插值表达式的学习</h1>
    <div id="aaa">
        <p v-cloak>{{msg1}}</p>
    
        <p>{{msg1}}</p>
    
        <p v-text="msg1"></p>
    
        <p v-text="msg2"></p>
    
        <p>{{msg2}}</p>
    
        <p v-html="msg2"></p>
    </div>
    
    <script src="../lib/vue.js" type="text/javascript"></script>
    <script type="text/javascript">
           var vm = new Vue({
               el:"#aaa",
               data:{
                   msg1:"hello",
                   msg2:"<p style='color:red'>我是一个p标签</p>"
               }
    
               })
    </script>
    </body>
    </html>
    

    运行结果:
    在这里插入图片描述
    4. v-bind:
    语法:在HTML元素的属性目前加上绑定标识,此时HTML的该属性的值就会根据属性值的名字从Model中获取数据
    示例:

    var vm = new Vue({
    el:"#app",
    data:{
    msg:“吾名”,
    name:“超强”
    }
    });
    注意:
    1.【v-bind:】用于绑定HTML元素属性的值
    2.【v-bind:】可以简写为:
    3.Vue框架会把使用了v-bind:标识的HTML的属性的值当作一个Javascript表达式来使用
    只要表达式合法,就能执行(当然,数据必须时字面量或来自于Model中)
    如:
    :value=“msg+‘你好’”
    :value=“msg+name”

    5. v-on:
    语法:在HTML的事件属性(不带on)前加上绑定标识,此时HTML触发此事件时,会执行事件中绑定的代码
    示例:
    点击下显示你好
    <button @click=“sayHello(name)”>点击下显示你好
    var vm = new Vue({
    el:"#app",
    data:{
    msg:“吾名”,
    name:“超强”
    },
    methods:{
    sayHello:function (name) {
    alert(“你好!”+name);
    }
    }
    });
    注意:
    1.常见的web网页的事件都支持绑定
    2.事件名字不带on
    3.事件触发的方法必须在methods中定义
    4.v-on标识的属性会被Vue解析为特定JavaScript,只要内容符合JavaScript规范
    都能正确执行(比如传入参数)
    4.【v-on:】等价于【@】

    代码测试:

    <!DOCTYPE html>
    <html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml" xmlns:v-on="http://www.w3.org/1999/xhtml">
    <head>
        <meta charset="UTF-8">
        <title>v-bind,v-on指令的学习</title>
        <script src="../lib/vue.js" type="text/javascript"></script>
    </head>
    <body>
    <h1>  </h1>
    
    <div id="app">
        <h2><label>用户名:<input type="text" name="username" v-bind:value="msg" /></label></h2><br>
        <h2>      用户名: <input type="text" name="aa0" :value="msg+name" ></h2><br>
    
        <button v-on:click=" sayHello(name)"> 点击显示你好</button><br>
        <button @click="sayHello(name)"> 点我</button><br>
    
    
    
    </div>
    
    <script type="text/javascript">
    
        var vm = new Vue({
            el:"#app",
            data:{
                msg:"吾名",
               name:"超强"
            },
            methods:{
                sayHello:function (name) {
                    alert("你好!"+name);
                }
            }
        });
    </script>
    </body>
    </html>
    

    运行结果:

    在这里插入图片描述

    接下来我们做一个小练习

    用Vue做一个跑马灯效果

    直接上代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>跑马灯</title>
        <script src="../lib/vue.js" type="text/javascript"></script>
    </head>
    <body>
    
    <div id="app">
    
        <button @click="light()">开始</button>
        <button @click="stop()">停止</button>
        <h3 v-html="info" style="color: aqua" ></h3>
    
    </div>
    <script type="text/javascript">
    
        var vm = new Vue({
            el:"#app",
            data:{
                info:" 嘿,姑娘,我想我是真的想你了,你是否也会想起我。",
                lightIntervalID:null
    
    
            },
            methods: {
                light() {
                    if (this.lightIntervalID !== null) {
                        return;
                    }
                    var _this = this;
                    this.lightIntervalID = setInterval(function () {
                            _this.info = _this.info.substring(1).concat(_this.info.charAt(0));
    
                        }, 200
                    )},
                stop() {
                    clearInterval(this.lightIntervalID);
                    this.lightIntervalID = null;
                }}
        });
    </script>
    </body>
    </html>
    

    来看下运行效果:(这里只展示静态效果)
    在这里插入图片描述

    展开全文
  • vue学习笔记(超详细)

    千次阅读 多人点赞 2020-12-18 16:38:35
    Vue基础认识Vue.jsVue安装方式Vue的MVVM二. Vue基础语法生命周期模板语法创建Vue, options可以放什么语法综合v-onv-for遍历数组v-model表单绑定v-model结合radio类型使用v-model结合checkbox类型使用v-model结合...

    文章目录

    一. Vue基础

    认识Vue.js

    Vue是一个渐进式的框架

    1. 渐进式意味着你可以将Vue作为你应用的一部分嵌套其中
    2. Vue的核心库以及其生态系统
    3. Core + Vue-router + Vuex

    Vue特点和Web开发常见高级功能

    1. 解耦视图和数据
    2. 可复用的组件
    3. 前端路由技术
    4. 状态管理
    5. 虚拟DOM

    Vue安装方式

    方式一. 直接CDN引入
    可以选择引入开发环境版本 / 生产环境版本

    // 开发环境版本, 包含了帮助的命令行警告
    <srcipt src='https://cdn.jsdeliver.net/npm/vue/dist/vue.js'></script>
    
    // 生产环境版本, 优化了尺寸和速度
    <srcipt src='https://cdn.jsdeliver.net/npm/vue'></script>
    

    方式二. 下载和引入

    // 开发环境
    https://vuejs.org/js/vue.js
    
    // 生产环境
    https://vuejs.org/js/vue.min.js
    

    方式三. NPM安装

    通过webpack和CLI的使用

    Vue的MVVM

    M: Model 数据模型

    • 数据层
    • 数据可能是固定的思数据, 更多的是来自服务器, 从网络上请求下来的数据

    V: View 视图模板

    • 视觉层
    • 在前端开发中, 通常是DOM层
    • 作用: 是给用户展示各种信息

    VM: View-Model 视图模型

    • 视图模型层
    • 是View和Model沟通的桥梁
    • 一方面实现了Data Binding (数据绑定), 讲Model的改变实时的反应到View中
    • 另一方面实现了DOM Listener (DOM监听), 当DOM发生一些时间 (点击, 滚动, touch等) 时, 可以监听到, 并在需要的情况下改变对应的Data

    二. Vue基础语法

    语法糖: 简写

    生命周期

    事物从诞生到消亡的整个过程

    模板语法

    创建Vue, options可以放什么

    • el
    • data
    • methods
    • 生命周期函数

    语法

    综合

    插值操作 Mustache

    • Mustache语法 (双大括号)
    • 可以直接写变量
    • 可以写简单的表达式

    v-once
    后面不需要跟任何表达式
    表示元素和组件只渲染一次, 不会随着数据的改变而变化

    v-html
    后面往往跟一个string类型
    会将string的html解析出来并渲染

    v-text
    与Mustache相似, 一般不用, 不灵活

    v-pre
    用于跳过这个元素和它子元素的编译过程, 用于显示原本的Mustache语法

    v-cloak
    在某些情况下, 我们浏览器可能会直接显示出未编译的Mustache标签

    v-bind
    作用: 动态绑定属性
    简写: :

    条件判断

    • v-if
    • v-else-if
    • v-else

    v-show

    • 当条件为false的时
      v-if: 指令的元素, 不会渲染到dom中
      v-show: dom增加一个行内样式display: none

    v-on

    作用: 绑定事件监听
    简写: @
    写法:

    • 没有参数的情况下, 可以不写(); 如果方法本身有一个参数, 会默认将原生事件event参数传递进去
    • 如果传入某个参数, 同时需要event时, 可以通过$event传入时间
    <div id="app">
      <h2>点击次数: {{counter}}</h2>
      <!--情况一: 方法没有参数-->
      <button @click="btnClick1">按钮1</button>
      <button @click="btnClick1()">按钮1</button>
    
      <!--情况二: 如果方法有参数-->
      <!--1.调用时不传入参数,会默认将event作为第一个参数传入-->
      <button @click="btnClick2">按钮2</button>
      <!--2.调用时不传入参数,那么参数为undefined-->
      <button @click="btnClick2()">按钮2</button>
    
      <!--情况三: 如果方法有参数,并且希望传入event-->
      <button @click="btnClick3(10, $event)">按钮3</button>
    </div>
    
    <script src="../../js/vue.js"></script>
    <script>
      const app = new Vue({
        el: '#app',
        data: {
          counter: 0
        },
        methods: {
          btnClick1() {
            console.log('按钮1被点击');
          },
          btnClick2(payload) {
            console.log('按钮2被点击', payload);
          },
          btnClick3(num, event) {
            console.log('按钮3被点击', num, event);
          }
        }
      })
    </script>
    
    修饰符作用实际调用
    .stop阻止事件冒泡event.stopPropagation()
    .prevent阻止默认事件event.preventDefault()
    {keyCode I keyAlias}监听某个键盘的键帽
    .native监听组件根元素的原生事件
    .once只触发一次回调

    v-for遍历数组

    // 遍历过程中, 没有使用索引值
    <li v-for="item in names">{{item}}</li>
    
    // 遍历过程中, 获取索引值
    <li v-for="(item, index) in names">{{index + 1}} - {{item}}</li>
    
    // 遍历对象的时候, 只有一个值, 活得的是value
    
    // 获取对象的key和value 
    <li v-for="(value, key) in names">{{value}} - {{key}}</li>
    
    // 获取对象的key和value和index 
    <li v-for="(value, key, index) in names">{{value}} - {{key}} - {{index}}</li>
    
    

    官方推荐, 使用v-for的时候, 加上一个 key属性
    图1
    key的作用是为了高效的更新虚拟DOM
    key要具有唯一性, 不然就没意义

    <li v-for="(item, index) in names" :key="item">{{index + 1}} - {{item}}</li>
    

    v-model表单绑定

    实现表单元素和数据的双向绑定

    <div id="app">
        <input type="text" v-model="message">
        {{message}}
    </div>
    
    cosnt app = new Vue({
        el: '#app',
        data: {
            message: '你好'
        }
    })
    
    // 界面的message数据改了, data里面的message就改变了, 是双向的
    

    可以将v-model用于textarea元素

    <textare v-model="message"></textare>
    <p>输入内容: {{message}} </p>
    

    其他方法实现双向绑定

    // v-bind绑定一个value属性
    // v-on指令给当前元素绑定input事件  
    // 下面代码 等同于 使用v-model
    
    <div id="app">
        <input type="text" :value="message" @input="message = $event.target.value">
        {{message}}
    </div>
    
    cosnt app = new Vue({
        el: '#app',
        data: {
            message: '你好'
        }
    })
    
    
    v-model结合radio类型使用
    <div id="app">
        <label for="male">
            <inout type="radio" id="male" value="男" v-model="sex">男
        </label>
        <label for="male">
            <inout type="radio" id="female" value="女" v-model="sex">nv
        </label>
        <h2>您选择的性别是: {{sex}}</h2>
    </div>
    
    cosnt app = new Vue({
        el: '#app',
        data: {
            sex: '男'
        }
    })
    
    v-model结合checkbox类型使用
    • 单选框
    // 需求: 是否同意该协议, 同意后下一步
    
    <div id="app">
        <label for="agree">
            <inout type="checkbox" id="agree"  v-model="isAgree">同意协议
        </label>
        <buttion :disabled="!isAgree">下一步</button>
    </div>
    
    cosnt app = new Vue({
        el: '#app',
        data: {
            isAgree: false
        }
    })
    
    // 点击文字部分也可以选中
    
    • 多选框
    <div id="app">
        <label for="agree">
            <inout type="checkbox" value="足球" v-model="hobbies">足球
        </label>
        <label for="agree">
            <inout type="checkbox" value="篮球" v-model="hobbies">篮球
        </label>
        <label for="agree">
            <inout type="checkbox" value="羽毛球" v-model="hobbies">羽毛球
        </label>
        <h2>您的爱好是: {{hobbies}}</h2>
    </div>
    
    cosnt app = new Vue({
        el: '#app',
        data: {
            isAgree: false,
            hobbies: []
        }
    })
    
    
    v-model结合select类型使用

    下拉框单选

    • v-model绑定的是一个值
    • 当选中option中一个时, 会将它对应的value赋值到mySelect中
    <select v-model="mySelect">
        <option value="apple">苹果<option>
        <option value="orange">橘子<option>
        <option value="banana">香蕉<option>
    </select>
    <p>您最喜欢的水果: {{mySelect}}</p>
    

    下拉框多选

    • v-model绑定的是一个数组
    • 当选择多个值时,会将选中的option对应的value添加到数组mySelect中
    <select v-model="mySelect" multiple>
        <option value="apple">苹果<option>
        <option value="orange">橘子<option>
        <option value="banana">香蕉<option>
    </select>
    <p>您最喜欢的水果: {{mySelect}}</p>
    

    值绑定

    含义: 动态的给value赋值
    1 在前面的value中的值, 都是在定义input的时候直接给定的
    2 但真实开发中, input的值可能是从网络获取或定义在data中的
    3 可以通过v-bind:value动态的给value绑定值

    <div id="app">
        <label v-for="item in nums" :for="item">
            <inout type="checkbox" value="item" :id="item" v-model="hobbies">{{item}}
        </label>
        <h2>您的爱好是: {{hobbies}}</h2>
    </div>
    
    cosnt app = new Vue({
        el: '#app',
        data: {
            hobbies: [],
            nums: ['篮球', '足球', '羽毛球']
        }
    })
    

    修饰符

    lazy修饰符

    • 前景: v-model默认是在input事件中实时同步输入框的数据的 (容易同步的过于频繁 )
    • 作用: 可以让数据只有在失去焦点或回车时才会更新
    <input type="text" v-model.lazy="输入">
    

    number修饰符

    • 前景: 默认情况下, 在输入框中无论输入字母还是数字, 都会被当做字符串类型进行处理
    • 作用: 当做数字类型进行处理

    trim修饰符

    • 前景: 输入的内容首位容易有空格
    • 作用: 可以过滤掉内容左右两边的空格

    检测数组更新

    Vue是响应式, 所以当数据发生变化时, Vue会自动检测数据变化, 视图会发生对应的更新.

    Vue中观察数据编译的方法 – 用它们改变数组会触发视觉更新

    数组响应式方法作用
    push()在数组中最后增加元素
    pop()删除数组中最后一个元素
    shift()删除数组中的第一个元素
    unshift()在数组最前面添加元素
    splice()删除 / 插入 / 替换元素
    sort()xx
    reverse()xx
    数组未响应作用
    filter()xxxxxxx
    concat()xxxxxxx
    slice()xxxxxxx

    计算属性 computed

    使用: 需要将多个数据结合起来进行显示的时候

    <div id='app'>
    
     // 使用拼接的方法 -- 语法太过繁琐
     <h2>{{firstName + '' + lastName}}</h2>
     <h2>{{firstName}} {{lastName}}</h2>
     
     // 使用方法
     <h2>{{getFullName()}}</h2>
     
     // 使用计算属性 -- 看起来最舒服 最好
     <h2>{{fullName}}</h2>
    </div>
    
    
    const app = new Vue({
        el: '#app',
        data:{
            firstName: 'li',
            lastName: 'er'
        },
        // 计算属性
        computed: {
            fullName: function () {
                reture this.firstName + '' + this.lastName
            }
        },
        // 方法
        methods: {
            getFullName () {
                reture this.firstName + '' + this.lastName
            }
        }
    })
    

    计算属性setter和getter

    <div id='app'>
     <h2>{{fullName}}</h2>
    </div>
    
    
    const app = new Vue({
        el: '#app',
        data:{
            firstName: 'li',
            lastName: 'er'
        },
        // 计算属性
        computed: {
            fullName: {
                // 一般没有set方法 
                set: function (value) {
                    
                },
                // 只读属性
                get: function () {
                    reture this.firstName + '' + this.lastName
                }
            }
        }
    })
    
    上面案例的写法是简写. 完整写法是调用了get();
    

    computed / methods区别

    多次使用的时候

    • methods: 每次都会调用
    • computed: 计算机会缓存, 不变的情况下只调用一次

    fulters过滤器

    <div id='app'>
     <h2 {{aaa | showA}}></h2>
    </div>
    
    
    const app = new Vue({
        el: '#app',
        data:{
            aaa: 'li'
        },
        // 过滤器
        fulters: {
            showA (value) {
            return;
        }
    })
    

    三. 组件化开发

    组件的基本介绍

    注册组件的基本步骤

    1. 调用Vue.extend()方法 - 创建组件构造器
    2. 调用Vue.component()方法 - 注册组件
    3. 在Vue实例的作用范围内 - 使用组件
    <div id="app">
        //使用组件
        <my-cpm></my-cpm>
    </div>
    
    //创建组件构造器
    const cpnC = Vue.extend({
        template: '
        <div>
            <h2>标题</h2>
            <p>模板</p>
        </div>'
    })
    
    //注册组件
    Vue.component('my-cpn', cpmC)
    
    const app = new Vue({
        el: '#app',
        data:{
            message: ''
        }
    })
    
    //
    

    全局组件 / 局部组件

    • 全局组件
    楼上的案例
    
    • 局部组件
    <div id="app">
        <my-cpm></my-cpm>
    </div>
    
    const cpnC = Vue.extend({
        template: '
        <div>
            <h2>标题</h2>
            <p>模板</p>
        </div>'
    })
    
    const app = new Vue({
        el: '#app',
        data:{
            message: ''
        },
        components: {
            cpn: cpnC
        }
    })
    

    父组件 / 子组件

    父子组件的错误用法: 以子标签的形式在Vue实例中使用

    1. 因为当子组件注册到父组件的components时, Vue会编译好父组件的模块
    2. 该模块的内容已经决定了父组件将要渲染的HTML (相当于父组件中已经有子组件中的内容了)

    语法糖

    Vue为了简化注册组件的过程, 提供了注册的语法塘, 省去了调用Vue.extend()的步骤. 而是可以直接使用一个对象来代替

    // 全局组件
    Vue.component('xxx', {
        template: '
        <div>
            哈哈哈哈
        </div>
        '
    })
    
    // 内部会自动调用Vue.extend()
    
    
    // 局部组件
    const app = new Vue({
        el: '#app',
        data: {
            meassage: '哈哈哈'
        },
        components: {
            'cpn': {
                template: '
                    <div>
                        哈哈哈哈
                    </div>
                '
            }
        }
    })
    

    组件模板抽离写法 (简单)

    • 方法一
      将模板放到
    // 注册一个全局组件  
    <div id="id">
        <cpn></cpn>
    </div>
    
    <script type="text/x-tenplate" id="cpn">
        <div>
            哈哈哈哈
        </div>
    </script>
    
    
    Vue.component('cpn', {
        template: '#cpn'
    })
    
    
    • 方法二
      放入标签
    <div id="id">
        <cpn></cpn>
    </div>
    
    <template id="cpn">
        <div>
            哈哈哈哈
        </div>
    </template>
    
    
    Vue.component('cpn', {
        template: '#cpn'
    })
    

    组件可以访问VUE实例数据吗?

    不能访问
    组件是一个单独功能模块的封装 - 这个模块有自己的HTML模板, 也应该有属性自己的数据data

    Vue.component('cpn', {
        template: '#cpn',
        data() {
            return {
                title: '哈哈哈'
            }
        }
    })
    
    // data属性必须是一个函数, 返回必须是一个对象
    
    组件中的data为什么必须是一个函数?

    https://www.bilibili.com/video/av59594689?p=57

    组件通讯

    父子组件的通讯

    父组件通过props向子组件传递数据

    • 写法一 (看看就行)
      用这种写法, 太别扭了
    <div id="app">
        <cpm v-bind:cmovies="movies" v-bind:cmessage="message"></cpn>
    </div>
    
    <template id="cpn">
        <div>
            <li v-for="item in cmovies">{{item}}</li>
            <h2>{{cmessage}}</h2>
        </div>
    </template>
    
    
    const cpn = {
        template: '#cpn',
        // 把数组里的当变量来看了
        props: ['cmovies', 'cmessage'],
        data () {
            return {}
        }
    }
    
    const app = new Vue({
        el: '#app',
        data: {
            meassage: '哈哈哈',
            movies: ['哈喽', '嗨', '哟哟']
        },
        components: {
            cpn
        }
    })
    
    • 写法二
    <div id="app">
        // 在使用组件的时候绑定
        // 不支持驼峰命名  cMovies要写成c-movies
        <cpm :c-movies="movies" :c-message="message"></cpn>
    </div>
    
    <template id="cpn">
        <div>
            <li v-for="item in cMovies">{{item}}</li>
            <h2>{{cMessage}}</h2>
        </div>
    </template>
    
    
    const cpn = {
        template: '#cpn',
        // 把数组里的当变量来看了
        props: {
            // 类型限制 
            // cMovies: Array,
            // cMeesage: String,
            
            // 类型限制 + 提供一些默认值 or  required表示必传值,不然报错
            cMessage: {
                type: String,
                default: '哈',
                required: true
            },  
            cMovies: {
                type: Array, // 对象or数组类型的时候, 默认值必须是个函数
                default() {
                    return {}
                }
            }
        }
        data () {
            return {}
        }
    }
    
    const app = new Vue({
        el: '#app',
        data: {
            meassage: '哈哈哈',
            movies: ['哈喽', '嗨', '哟哟']
        },
        components: {
            cpn
        }
    })
    

    子组件通过事件向父组件发送数据

    // 父组件模板
    <div id="app">
        // 2.父组件监听一个事件
        // 不能写驼峰
        <cpm @itemclick="cpnClick"></cpn>
    </div>
    
    // 子组件模板
    <template id="cpn">
        <div>
            <button v-for="item in categories" 
                    @click="btnClick(item)">
                {{item.name}}
            </button>
        </div>
    </template>
    
    
    // 子组件
    const cpn = {
        template: '#cpn',
        data() {
            return {
                categories: [
                    {id: 'aaaa', name: '热门推荐'},
                    {id: 'bbbb', name: '手机数码'},
                    {id: 'cccc', name: '家用家电'},
                    {id: 'dddd', name: '电脑办公'},
                ]
            }
        },
        methods: {
            btnClick(item) {
                // 要把item传给父组件
                // 1. 发送一个事件 (自定义事件)
                // 会把item当成默认的传到父组件去
                this.$emit('itemclick', item)
            ]
        }
    }
    
    // 父组件
    const app = new Vue({
        el: '#app',
        data: {
            meassage: '哈哈哈'
        },
        components: {
            cpn
        },
        methods: {
            // 3. 父组件监听的事件 
            cpnClick(item) {
                console.log('成功了', item);
            }
        }
    })
    

    父子组件通讯-双向绑定

    https://www.bilibili.com/video/av59594689?p=63

    父子组件的访问方式

    父组件访问子组件

    • $children: 拿所有子组件
    • $refs: 拿指定的子组件
    // this.$children是一个数组类型, 它包含所有子组件对象  
    // 通过遍历, 取出所有子组件的message状态  
    
    <div id="app">
        <cpm></cpn>
        <cpm></cpn>
        // 使用$refs的时候, 在想要访问的子组件上添加ref属性
        <cpm ref="aaa"></cpn>
        <buttun @click="btnClick">按钮</button>
    </div>
    
    <template id="cpn">
        <div>
            我是子组件
        </div>
    </template>
    
    const app = new Vue({
        el: '#app',
        data: {
            meassage: '哈哈哈'
        },
        methods: {
            btnClick() {
                cosole.log(this.$children);
                // $children 的使用方法, 一般用的少, 下标不固定
                // this.$children[0].showMessage();
                // this.$children[0].name;
                
                //for (let c of this.$children) {
                //    console.log(c.name); // 我是子组件的name
                //    c.showMessage(); // showMessage
                //}
                
                // $refs 的使用方法 => 对象类型, 默认是一个空的对象
                console.log(this.$refs.aaa.name); // 我是子组件的name
            }
        }
        components: {
            cpn: {
                template: "#cpn",
                data() {
                    return {
                        name: '我是子组件的name'
                    }
                },
                methods: {
                    showMessage() {
                        cosole.log('showMessage');
                    }
                }
            }
        }
    })
    
    

    子组件访问父组件

    • $parent: 上一级父组件
    • $root: 根组件
    <div id="app">
        <cpm></cpn>
    </div>
    
    <template id="cpn">
        <div>
            <ccpn></ccpn>
        </div>
    </template>
    
    <template id="ccpn">
        <div>
            <h2>我是子组件</h2>
            <button @click="btnClick">按钮</button>
        </div>
    </template>
    
    const app = new Vue({
        el: '#app',
        data: {
            meassage: '哈哈哈'
        },
    
        components: {
            cpn: {
                template: "#cpn",
                data() {
                   return {
                       name: '我是cpn的name'
                   } 
                },
                conmponents: {
                    ccpn: {
                        template: '#ccpn',
                        btnClick() {
                            // 1. 访问父组件$parent
                            // 不建议这么写, 一层套一层 复用性太差
                            console.log(this.$parent);  
                            console.log(this.$parent.name);  // 我是cpn的name
                            
                            // 2. 访问根组件 $root
                            console.log(this.$root.meassage);   // 哈哈哈
                        } 
                    }
                }
            }
        }
    })
    

    跨级组件通信之provide/inject

    provide选项允许我们指定我们想要提供给后代组件的数据/方法。

    provide: function () {
      return {
        fun: this.fun, //方法
        name: 'Bob', // 数据
      }
    }
    

    然后在任何后代组件里,我们都可以使用inject选项来接收指定的我们想要添加在这个实例上的属性:

    //接受方法
    inject: ['fun']
    //接受数据
    inject:['name']
    

    插槽slot

    作用:
    组件的插槽是为了让我们封装的组件更加具有扩展性
    让使用者可以决定组件内部的一些内容到底展示什么

    基本使用

    1. 插槽的基本使用
    2. 插槽的默认值 button
    3. 如果有多个值同时放入到组件进行替换时, 一起作为替换元素
    <div id="app">
        // 使用默认值替换
        <cpm></cpn>
        // 插槽替换的元素
        <cpm><span>这是替换的内容呀</span></cpn>
        // 多个元素
        <cpm>
            <div>第一个元素</div>
            <p>第二个元素</p>
        </cpn>
    </div>
    
    <template id="cpn">
        <div>
            <div>这个是标题</div>
            <p>这个是内容</p>
            // 放入插槽
            <slot>默认值元素</slot>
        </div>
    </template>
    
    const app = new Vue({
        el: '#app',
        data: {
            meassage: '哈哈哈'
        },
        components: {
            cpn: {
                template: '#cpn'
            }
        }
    })
    

    具名插槽

    在多个插槽的情况下, 替换制定插槽的内容

    <div id="app">
        <cpm></cpn>
        <cpm><button slot="left">返回</button></cpn>
        <cpm><span slot="center">中间标题</span></cpn>
    </div>
    
    <template id="cpn">
        <div>
            <slot name="left"><span>左边</span></slot>
            <slot name="center"><span>中间</span></slot>
            <slot name="right"><span>右边</span></slot>
        </div>
    </template>
    
    const app = new Vue({
        el: '#app',
        data: {
            meassage: '哈哈哈'
        },
        components: {
            cpn: {
                template: '#cpn'
            }
        }
    })
    

    作用域插槽

    编译作用域

    父组件模板所有的东西都在父级作用域内编译, 子组件模板的所有东西会在子级作用域内编译

    <div id="app">
        <cpm v-show="isShow">我是一</cpn> // true
    </div>
    
    <template id="cpn">
        <div>
            <p v-show="isShow">我是二</p> // false
        </div>
    </template>
    
    const app = new Vue({
        el: '#app',
        data: {
            isShow: true // Vue实例中的属性
        },
        components: {
            cpn: {
                template: '#cpn',
                data() {
                    return {
                         isShow: false // 子组件中的属性
                    }
                }
            }
        }
    })
    

    作用域插槽使用

    父组件替换插槽的标签, 但是内容由子组件来提供

    需求: 
    1. 子组件中包括一组数据, num: ['1', '2', '3', '4', '5']
    2. 需要在多个界面展示 
       某些界面是以水平方向展示
       某些界面是以列表形式展示
       某些界面直接展示一个数组 
    3. 内容在子组件, 希望父组件告诉我们如何展示, 怎么办?
       利用slot作用域插槽就行了
       
    <div id="app">
        <cpm></cpn> 
        <cpm>
            // 2. 获取子组件中的num
            <template slot-scope="slot">
                // 根据之前的起名来取  如 slot.aaa
                <span  v-for="item in slot.data">{{item}}</span>
            </template>
        </cpn> 
        <cpm></cpn> 
    </div>
    
    <template id="cpn">
        <div>
            // 1. slot定义  'data'可以随便起名 如 :aaa="num"
            <slot :data="num">
                <ul>
                    <li v-for="item in num">{{item}}</li>
                </ul>
            </slot>
        </div>
    </template>
    
    const app = new Vue({
        el: '#app',
        data: {
            message: '哈哈哈'
        },
        components: {
            cpn: {
                template: '#cpn',
                data() {
                    return {
                        num: ['1', '2', '3', '4', '5']
                    }
                }
            }
        }
    })
    

    四. webpack

    是一个JS应用的静态模块打包工具

    webpack安装

    webpack依赖node环境, node环境为了可以正常执行很多代码, 必须包含各种依赖的包,npm工具是为了管理包

    // node官网下载就行了
    
    // 查看node版本  
    npm -v  
    
    // 全局安装webpack  (指定版本)
    nom install webpack@3.6.0 -g
    
    // 局部安装webpack  
    // --save-dev 是开发时依赖, 项目打包后不需要继续使用
    cd 对应目录
    npm install webpack@3.6.0 --save-dev
    
    
    // 查看webpack安装版本
    webpack -v
    
    // 安装webpack后错误提示 / VUE项目安装同理
    webpack : 无法加载文件  C:\Users\球球\AppData\Roaming\npm\webpack.ps1,因为在此系统上禁止运行脚本。有关详细信息,请参阅 https:/go.microsoft.com/fwlink/?LinkID=135170 中的 about_Execution_Policies。        
    + webpack -v
    
    解决方案 
    (1)以管理员身份运行vs code
    
    (2)在终端执行:get-ExecutionPolicy,显示Restricted(表示状态是禁止的)
    
    (3)在终端执行:set-ExecutionPolicy RemoteSigned
    
    (4)在终端执行:get-ExecutionPolicy,显示RemoteSigned
    

    webpack配置

    https://www.bilibili.com/video/av59594689?p=77

    创建: webpack.config.js 文件

    // 为了动态获取打包后的路径, path 在node包里面 -- 看下面一段代码 装包
    const path = require('path')
    
    module.exports = {
        entry: './src/main.js', // 要打包的文件
        output: {
            path: path.resolve(__dirnmae, 'dist'), // 打包后的文件  要写绝对路径--动态获取路径
            filename: 'bundle.js'
        }
    }
    
    

    node装包

    // 初始化--生成
    npm init
    
    // 生成的文件可能有中文 / 符号  可以改名 
    meetwebpack 
    
    ... 后面全部回车, 一路通过. OK就行  
    ... 生成 package.json文件
    
    // package.json文件里面也有依赖的文件  
    
    

    package.json文件

    "version" -- 版本号  
    
    // 本地安装webpack (之前是全局安装)  -- 开发时依赖  运行时依赖
    npm install webpack@3.6.0 --save-dev
    
    // 开发时依赖 (本地安装webpack后, 重新打开package.json文件会出现)
    "devDependencies": {  
        "webpack": "^3.6.0"  
    }
    
    // 使用vue之后, 会有 (运行时依赖)
    "dependencise": {
        "webpack": "^3.6.0"  
    }
    
    ---
    
    // 执行脚本的配置
    "scripts": {
        "test": '',    
        "build": "webpack" 
    }
    
    npm run test
    npm run build
    

    CSS Less Img ES6转Es5 Vue 处理

    webpack本身来说无法处理css, 图片, 高级ES6转化ES5的能力, 所以需要webpack扩展对应的loader就好了

    loader使用过程

    • 步骤一: 通过npm安装需要使用的loader (查官网)
    • 步骤二: 在webpack.config.js中国年的modules关键字下配置

    main.js

    // 1. 使用commonjs的模块化规范  
    const {add, mul} = require('./js/mathUtils.js')
    
    // 2. 使用ES6的模块化的规范 
    import {name, age} from "./js/info"
    
    // 3. 依赖CSS文件  
    require('./css/normal.css')
    

    具体的直接看视频吧

    认识Plugin

    plugin是什么

    • plugin是插件的意思, 用户对某个现有的架构进行扩展
    • webpack中的插件就是对webpack现有功能的各种扩展, 比如打包优化, 文件压缩等

    loader和plugin区别

    • loader - 主要用户转换某些类型的模块, 它是一个转换器
    • plugin - 是插件, 是对webpack本身的扩展, 它是一个扩展器

    plugin的使用过程

    • 步骤一: 通过npm安装需要使用的plugin (某些webpack已经内置的插件不需要安装)
    • 步骤二: 在webpack.config.js中的plugins中配置插件

    常用插件介绍

    添加版权的Plugin

    名字: BannerPlugin (webpack自带)

    // 按照下面方法修改webpack.config.js文件
    const path = require('path')
    const webpack = require('webpack')
    
    module.exports = {
        ...
        plugins: [
            new webpack.BannerPlugin('最终版权归coder所有')
        ]
    }
    
    // 重新打包程序, 查看bundle.js文件的头部, 看到如下信息
    /*! 最终版权归coder所有 */
    
    

    打包html的plugin

    我们知道index.html文件是存在项目的根目录下的, 在真实发布项目是, 发布的却是dist文件夹中的内容, 所以需要将index.html文件打包到dist文件夹中.

    名字: htmlWebPlugin

    1. 自动生成一个index.html文件 (可以指定模板来生成)
    2. 将打包的js文件, 自动通过script标签插入到body中
    npm install html-webpack-plugin --save-dev
    
    // 修改webpack.config.js文件
    const path = require('path')
    const webpack = require('webpack')
    const htlmWwbPlugin = require('html-webpack-plugin')
    
    module.exports = {
        ...
        plugins: [
            new webpack.BannerPlugin('最终版权归coder所有'),
            new htmlWebPlugin({
                template: 'index.html' // 这里的template表示根据什么模板快来生index.html
            })
        ]
    }
    
    

    JS压缩的plugin

    项目发布之前, 需要对JS等我呢渐进性压缩处理 (代码丑化)

    名字: uglifyjs-webpack-plugin
    PS: 指定版本1.1.1 和CLI2保持一致

    npm install uglifyjs-webpack-plugin@1.1.1 --save--dev 
    
    // 修改webpack.config.js文件
    const path = require('path')
    const webpack = require('webpack')
    const uglifyJsPlugin = require('uglifyjs-webpack-plugin')
    
    module.exports = {
        ...
        plugins: [
            new webpack.BannerPlugin('最终版权归coder所有'),
            new uglifyJsPlugin()
        ]
    }
    
    // 查看打包的bundle.js文件, 是已经被压缩的文件
    

    搭建本地服务器

    webpack提供一个可选的本地开发服务器(基础node.js搭建), 内部使用express框架, 可以实现让浏览器自动刷新显示我们修改后的结果

    它是一个单独的模块, 在webpack中使用需要先安装

    npm install --save-dev webpack-dev-server@2.9.1  
    
    • devserver也是作为webpack中的一个选项, 选项本身可设置如下属性
      contentBase: 为哪一个文件夹提供本地服务, 默认是根文件夹. 可填写 ./dist
      port: 端口号
      inline: 页面实时刷新
      historyApiFallback: 在SPA页面中, 依赖HTML5的history模式
    // 修改webpack.config.js文件
    module.exports = {
        ...
        devServer: {
            contentBase: './dist',
            inline: true
        }
    }
    
    // package.json文件中再配置一个scripts,  open参数表示直接打开浏览器 
    "scripts": {
        ...
        "dev": "webpack-dev-server --open" 
    }
    

    webpack配置分离

    很多配置开发时需要, 发布时不需要,反之一样, 所以要做分离

    1. 在build文件夹中, 建立一个base.config.js文件 --> 公共配置
    2. 在build文件夹中, 建立一个dev.config.js文件 --> 开发配置
    3. 在build文件夹中, 建立一个prod.config.js文件 --> 发布配置
    4. 复制webpack.config.js文件内容 -> 上面三个文件
    5. 按照区分 --> 进行文件夹内配置的删除
    6. 装一个插件
    npm install webpack-merge --save-dev 
    
    1. https://www.bilibili.com/video/av59594689?p=89 (9m)

    五. Vue CLI

    CLI 是(Command-Line Interface) 命令行界面, 俗称脚手架
    Vue CLI是官方发布的vue.js项目脚手架, 可以快速搭建vue开发环境以及webpack配置

    Vue CLI 使用前提 - 安装node 安装webpack

    Vue CLI的使用

    // 安装Vue脚手架 
    npm install -g @vue/cli  
    
    // 上面安装的是Vue CLI3.0版本, 想要按照Vue CLI2的方式初始化项目时需要进行下列的命令 
    // 拉取2.x模板
    // 'vue init' 的运行效果将会跟 'vue-cli@2.x' 相同
    npm install -g @vue/cli-init  
    
    // Vue CLI2初始化项目  project -> 项目名称 英文
    vue init webpack project
    
    // Vue CLI3初始化项目 project -> 项目名称 英文
    vue create project
    

    Vue CLI2

    // 创建Vue CLI2项目
    vue init webpack vuecli2   
    
    ? Project name vuecli2 --> 项目名字
    ? Project description test vue cli2 --> 项目描述  
    ? Author  huqinxue --> 作者
    ? Vue build --> 详解看下面一段
    ? Install vue-router? (Y/n)  Y
    ? Use ESlint to lint your code? (Y/n) Y
      ? Pick an ESLint preset --> 选择ESlint的规范 Standard  
    ? Set up unit tests (Y/n)  --> 单元测试 
    ? Setup e2e tests with Nightwatch? (Y/n)  --> 端对端测试 
    ? Yes, use NPM
      Yes, use Yarn
      
    

    ❤ Vue build的选择-runtiome+compilter和runtiome-only 区别

    https://www.bilibili.com/video/av59594689?p=95
    ① runtiome+compilter 和 runtiome-only 区别
    ② render函数的使用

    runtiome+compilter 和 runtiome-only 区别只在 main.js文件里面
    
    // runtiome+compilter 流程 
    template -> ast (抽象语法树) -> render -> Virtual dom (虚拟DOM) -> UI
    
    // runtiome-only 流程  (1. 性能更好  2. 代码量更少)
    render -> Virtual dom (虚拟DOM) -> UI 
    
    rebder函数的使用 (runtiome-only)
    
    -- 使用方式一: 
    return createElement('标签', '相关数据对象, 可不传', ['内容数组']) 
    
    new Vue({
        el: "#app",
        render: (createElement) => {
            //render函数基本使用
            return createElement('div', {class: 'box'}, ['codewhy']) 
            //嵌套render函数
            return createElement('div', {class: 'box'}, ['codewhy', createElement('h2', ['标题啊'])]) 
        }
    })
    
    -- 使用方式二: 传入一个组件对象  
    const cpn = Vue.component('cpn', {
        template: '<div>我是cpn组件</div>',
        data () {
            return {
                
            }
        }
    })
    
    new Vue({
        el: "#app",
        render: (createElement) => {
            return createElement(cpn) 
        }
    })
    

    目录结构解析

    https://www.bilibili.com/video/av59594689?p=92

    // package.json文件  -> 查看命令的写法
    "scripts": {
        "dev": --> build / webpack.dev.conf.js 
        "start":
        "lint": 
        "build":  --> build / build.js + webpack.prod.conf.js 
    }  
    
    // config文件夹  
    都是定义的一些变量  
    
    // node_moudules文件夹  
    依赖的包 
    
    // src文件夹  
    开发的文件夹  
    
    // static文件夹  
    静态资源, 打包后会原封不动的放到build文件夹中
    
    
    // .bobelrc文件
    依赖evn的时候产生的文件
    
    // .editorconfig文件  
    快速的一些习惯, 比如空格是2个字符还是4个字符
    
    // .eslintignore文件
    eslint可以忽略的问题  
    
    ! 看视频吧, 写的..累
    图2
    

    CLI错误

    // 安装不成功的时候, 可以清除缓存 (要从管理员权限打开)
    npm clean cache --force
    

    Vue CLI3

    3 是基于 webpack 4 打造,2还是 webapck 3
    3 的设计原则是“0配置”,移除的配置文件根目录下的,build和config等目录
    3 提供了 vue ui 命令,提供了可视化配置,更加人性化
    移除了static文件夹,新增了public文件夹,并且index.html移动到public中

    // 创建Vue CLI3项目
    vue create program 
    
    ? Please pick a preset:
    > default (babel, eslint)
    > Manually select features //手动选择功能
    
    • default路线
    // 用哪个下载依赖
    Pick the package manager to use when installing dependencies: 
      Use Yarn
    > Use NPM
      
    Vue CLI v3.0.0-alpha.5
    ✨  reating project in E:\git\note\my-project. // 创建项目
    �  Initializing git repository... // 初始化git库
    ⚙  Installing CLI plugins. This might take a while... // 安装脚手架插件  
    
    其实这个过程中还会判断你对npm/yarn源的连接速度,询问你是否切换至淘宝镜像
    Your connection to the the default npm registry seems to be slow.
    Use https://registry.npm.taobao.org for faster installation? 
     
    完成之后我们可以看到除node_modules之外的目录结构变成了
    │  package-lock.json
    │  package.json
    ├─public
    │      favicon.ico
    │      index.html
    └─src
        │  App.vue
        │  main.js
        ├─assets
        │      logo.png
        └─components
                HelloWorld.vue
    
    • Manually select features路线
    ? Check the features needed for your project: (Press <space> to select, <a> to toggle all, <i> to invert selection)
    >( ) TypeScript
     ( ) Progressive Web App (PWA) Support
     ( ) Router
     ( ) Vuex
     ( ) CSS Pre-processors
     ( ) Linter / Formatter --> ESLint
     ( ) Unit Testing
     ( ) E2E Testing
    
    PS: 空格选中/反选 回车确认 
     
    看到可以自由组合现在所需的功能了。
    创建的过程中会询问配置文件保存位置是config.js还是package.json,但是其中也是一些简单的配置
    
    // 这些配置文件存放方式  
    ? Where do you prefer placing config for Bable, PostCSS, ESLint, etc,> (Use arrow keys)  
    > In dedicated config files  --> 单独存放到一个文件   
      In package.json
    
    // 刚刚自定义的配置是否需要保存一个模板 (下次会在创建后出现)  
    // 可以删除的, 在.vuerc文件里  
    // 文件后面有rc (run command 运行终端的意思)  
    ? Save this as a preset for future projects? (Y/n)
    ...
    
    // 用哪个下载依赖
    Pick the package manager to use when installing dependencies: 
      Use Yarn
    > Use NPM
    

    目录结构

    // public 文件夹  
    相当于Vue CLI2中的 static文件夹   
    存储不压缩的内容  
    
    // .babelrc 文件 
    ES语法转换
    
    // .browserslistrc 文件  
    浏览器相关支持情况  
    
    // .gitigore 文件  
    git忽略文件  
    
    // .postcssrc.js 文件  
    CSS相关转换
    

    配置

    https://www.bilibili.com/video/av59594689?p=97

    // 启动本地服务器 
    vue ui 
    
    // 自定义配置   
    看视频吧~~
    

    六. vue-router

    路由是一个网络工程里面的术语
    路由(routing)就是通过互联的网络把信息从源地址传输到目的地址的活动 — 维基百科

    路由器提供了两种机制: 路由和转送

    • 路由是决定数据包从来源到目的地的路径
    • 转送将输入端的数据转移到合适的输出端

    路由中有一个非常重要的概念叫路由表

    • 路由表本质上就是一个映射表, 决定了数据包的指向

    前端渲染 + 后端渲染

    https://www.bilibili.com/video/av59594689?p=100

    后端路由阶段

    早期的网站开发整个HTML页面是由服务器来渲染

    • 服务器直接生产渲染好对应的HTML页面, 返回给客户端进行展示
    • 一个页面有自己对应的网址, 也就是URL.
    • URL会发送到服务器,
    • 服务器会通过正则对该URL进行匹配,并且最后交给一个Controller进行处理.
    • Controller进行各种处理, 最终生成HTML或者数据, 返回给前端.
      这就完成了一个IO操作.

    上面的操作, 就是后端路由

    • 当我们页面中需要请求不同的路径内容时, 交给服务器来进行处理, 服务器渲染好整个页面, 并且将页面返回给客户顿
    • 这种情况下渲染好的页面, 不需要单独加载任何的js和css, 可以直接交给浏览器展示, 这样也有利于SEO的优化

    后端路由的缺点

    • 一种情况是整个页面的模块由后端人员来编写和维护的
    • 另一种情况是前端开发人员如果要开发页面,需要通过PHP和Java等语言来编写页面代码
    • 而且通常情况下HTML代码和数据以及对应的逻辑会混在一起, 编写和维护都是非常糟糕的事情

    前端路由阶段

    前后端分离阶段

    • 随着Ajax的出现, 有了前后端分离的开发模式
    • 后端只提供API来返回数据, 前端通过Ajax获取数据,并且可以通过JavaScript将数据渲染到页面中
    • 这样做最大的优点就是前后端责任的清晰, 后端专注于数据上,前端专注于交互和可视化上
    • 当移动端(iOS/Android)出现后, 后端不需要进行任何处理, 依然使用之前的一套API即可
    • 目前很多的网站依然采用 这种模式开发

    前端渲染
    浏览器中显示的网页中大部分内容, 都是由前端写的JS代码在浏览器中执行,最终渲染出来

    SPA单页面富应用阶段

    • 其实SPA最主要的特点就是在前后端分离的基础上加了一层前端路由.
    • 也就是前端来维护一套路由规则

    前端路由的核心
    改变URL,但是页面不进行整体的刷新

    如何实现

    1. URL的hash
      URL的hash也就是锚点(#), 本质上是改变window.location的href属性.
      通过直接赋值location.hash来改变href, 但页面不刷新

    2. HTML5的history模式
      history接口是HTML5新增的, 它有五种模式改变URL而不刷新页面
      ①. history.pushState() --> 入栈
      ②. history.replaceState()
      ③. history.go()
      ④. history.back() 等价于 history.go(-1) --> 后退
      ⑤. history.forward() 等价于 history.go(1) --> 前进

    综合总结提问

    1. 什么是前端渲染, 什么是后端渲染?

    前端渲染

    指的是后端返回JSON数据,前端利用预先写的html模板,循环读取JSON数据,拼接字符串(es6的模板字符串特性大大减少了拼接字符串的的成本),并插入页面。

    • 好处:网络传输数据量小。不占用服务端运算资源(解析模板),模板在前端(很有可能仅部分在前端),改结构变交互都前端自己来了,改完自己调就行。

    • 坏处:前端耗时较多,对前端工作人员水平要求相对较高。前端代码较多,因为部分以前在后台处理的交互逻辑交给了前端处理。占用少部分客户端运算资源用于解析模板。

    后端渲染

    前端请求,后端用后台模板引擎直接生成html,前端接受到数据之后,直接插入页面。

    • 好处:前端耗时少,即减少了首屏时间,模板统一在后端。前端(相对)省事,不占用客户端运算资源(解析模板)

    • 坏处:占用服务器资源。

    前端渲染与后端渲染对比:

    • 后端渲染:

    页面呈现速度:快,受限于用户的带宽
    流量消耗:少一点点(可以省去前端框架部分的代码)
    可维护性:差(前后端东西放一起,掐架多年,早就在闹分手啦)
    seo友好度:好
    编码效率:低(这个跟不同的团队不同,可能不对)

    • 前端渲染:

    页面呈现速度:主要受限于带宽和客户端机器的好坏,优化的好,可以逐步动态展开内容,感觉上会更快一点

    流量消耗:多一点点(一个前端框架大概50KB)当然,有的用后端渲染的项目前端部分也有在用框架

    可维护性:好,前后端分离,各施其职,代码一目明了。
    SEO友好度:差,大量使用ajax,多数浏览器不能抓取ajax数据。
    编码效率:高,前后端各自只做自己擅长的东西,后端最后只输出接口,不用管页面呈现,只要前后端人员能力不错,效率不会低

    2. 什么是前后端分离

    现在 Web 服务器不再处理任何业务,它接收到请求后,经过转换,发送给各个相关后端服务器,将各个后端服务器返回的,处理过的业务数据填入 HTML 模板,最后发送给浏览器。Web 服务器和后端服务器间,可以选用任何你觉得合适的通信手段,可以是 REST,可以是 RPC,选用什么样的通信手段,这是另一个议题了。

    这样,前端人员和后端人员约定好接口后,前端人员彻底不用再关心业务处理是怎么回事,他只需要把界面做好就可以了,后端人员也不用再关系前端界面是什么样的,他只需要做好业务逻辑处理即可。服务的切离,代码管理,服务部署也都独立出来分别管理,系统的灵活性也获得了极大的提升。

    注意,这不是个微服务架构,那是另外一个议题了

    总结,任何系统架构设计,实际上是对组织结构在系统上进行映射,前后端分离,就是在对前端开发人员和后端开发人员的工作进行解耦,尽量减少他她们之间的交流成本,帮助他她们更能专注于自己擅长的工作。

    最后是几个常见误解的说明

    1. 前后端分离是说浏览器和后端服务分离吗?

    不是,前后端分离里的前端不是浏览器,指的是生成 HTML 的那个服务,它可以是一个仅仅生成 HTML 的 Web 服务器,也可以是在浏览器中通过 JS 动态生成 HTML 的 单页应用。实践中,有实力的团队往往在实现前后端分离里时,前端选用 node 服务器,后端选用 C#、Java 等(排名不分先后)

    1. 前后端分离是种技术吗?

    不是,前后端分离是种架构模式,或者说是最佳实践。所谓模式就是大家这么用了觉得不错,你可以直接抄来用的固定套路。

    1. 前后端分离是最佳实践吗?

    看你团队和项目的情况,如果是短平快的小项目,真的没必要。如果是面向简历开发,那绝对在任何时候都应该使用前后端分离这种架构。

    3. 什么是前端路由, 什么是后端路由?

    A. 什么是前端路由?

    很重要的一点是页面不刷新,前端路由就是把不同路由对应不同的内容或页面的任务交给前端来做,每跳转到不同的URL都是使用前端的锚点路由. 随着(SPA)单页应用的不断普及,前后端开发分离,目前项目基本都使用前端路由,在项目使用期间页面不会重新加载

    B. 什么是后端路由?

    浏览器在地址栏中切换不同的url时,每次都向后台服务器发出请求,服务器响应请求,在后台拼接html文件传给前端显示, 返回不同的页面, 意味着浏览器会刷新页面,网速慢的话说不定屏幕全白再有新内容。后端路由的另外一个极大的问题就是 前后端不分离。

    优点:分担了前端的压力,html和数据的拼接都是由服务器完成。

    缺点:当项目十分庞大时,加大了服务器端的压力,同时在浏览器端不能输入制定的url路径进行指定模块的访问。另外一个就是如果当前网速过慢,那将会延迟页面的加载,对用户体验不是很友好。

    C. 什么时候使用前端路由?

    在单页面应用,大部分页面结构不变,只改变部分内容的使用

    D. 前端路由有什么优点和缺点?

    优点:

    • 用户体验好,和后台网速没有关系,不需要每次都从服务器全部获取,快速展现给用户

    • 可以再浏览器中输入指定想要访问的url路径地址。

    • 实现了前后端的分离,方便开发。有很多框架都带有路由功能模块

    缺点:

    • 使用浏览器的前进,后退键的时候会重新发送请求,没有合理地利用缓存

    • 单页面无法记住之前滚动的位置,无法在前进,后退的时候记住滚动的位置

    vue-router基本了解

    认识vue-router

    目前前端流行的三大框架, 都有自己的路由实现:

    • Angular: ngRouter
    • React: ReactRouter
    • Vue: vue-router

    vue-router是Vue.js官方的路由插件,它和vue.js是深度集成的,适合用于构建单页面应用
    我们可以访问其官方网站对其进行学习: https://router.vuejs.org/zh/
    vue-router是基于路由和组件的
    路由用于设定访问路径, 将路径和组件映射起来.
    在vue-router的单页面应用中, 页面的路径的改变就是组件的切换.

    安装vue-router

    方法一: 创建项目时就选择路由

    方法二: 自己安装路由

    https://www.bilibili.com/video/av59594689?p=102

    因为我们已经学习了webpack, 后续开发中我们主要是通过工程化的方式进行开发的.
    在后续, 我们直接使用npm来安装路由即可

    • 步骤一: 安装vue-router
    npm install vue-router --save
    
    • 步骤二: 在模块化工程中使用它(因为是一个插件, 可以通过Vue.use()来安装路由功能)
    在src文件夹中创建router文件夹  
    在router文件夹中创建index.js文件
    
    -- index.js  配置路由相关的信息
    // 导入路由对  
    import Vue from ‘vue’
    import VueRouter from ‘vue-router’
    
    // 2. 通过Vue.use(插件)安装插件
    Vue.use(VueRouter)
    
    // 3. 创建路由实例--> VueRouter对象  
    // 3.2 把router抽象出来
    const routes = [
    ]
    
    const app = new VueRouter({
        // 3.1 配置路由和组件之间的映射配置 
        router
    })
    
    // 4. 将router对象传入到Vue实例  
    export defualt router
    
    -- main.js 文件 
    // 导入router
    import router from './router'
    
    // 挂载
    new Vue({
        el: '#app',
        router,
        render: h => h(App)
    })
    
    

    vue-router的使用

    通过和

    • : 该标签是一个vue-router中已经内置的组件, 它会被渲染成一个标签
    • : 该标签会根据当前的路径, 动态渲染出不同的组件
    • 网页的其他内容, 比如顶部的标题/导航, 或者底部的一些版权信息等会和处于同一个等级
    • 在路由切换时, 切换的是挂载的组件, 其他内容不会发生改变
    -- router - index.js文件 
    
    import Vue from ‘vue’
    import VueRouter from ‘vue-router’
    import Home from '../components/Home'
    import About from '../components/About'
    
    Vue.use(VueRouter)
    
    const routes = [
        {
            path: '/', 
            // 页面默认的时候, redirect重定向
            redirect: './home'
        },
        {
            path: '/home',
            component: Home
        },
        {
            path: '/about',
            component: About
        }
    ]
    
    const app = new VueRouter({
        router,
        // 改变模式 默认是hash模式(有#号)
        mode: 'history'
    })
    
    export default router
    
    -- App.vue文件
    
    <div>
        <router-link to="/home"></router-link>
        <router-link to="/about"></router-link>
        <router-view></router-view>
    </div>
    

    router-link属性补充

    属性 to
    用于指定跳转的路径

    <router-link to='/home'></router-link>
    

    属性 tag
    可以指定之后渲染成什么组件

    // 会被渲染成一个<li>元素
    <router-link to='/home' tag='li'></router-link>
    ...
    

    属性 replace
    不会留下history记录, 所以指定replace的情况下, 后退键返回不能返回到上一个页面中

    <router-link to='/home' replace></router-link>
    

    属性 active-class
    设置active-class可以修改默认的名称

    // home被选中状态 
    // 当<router-link>对应的路由匹配成功时, 会自动给当前元素设置一个router-link-active的class
    <router-link to='/home' router-link-active></router-link>
    <router-link to='/about'></router-link>
    
    // 修改默认的名称  
    <router-link to='/home' active-class="active"></router-link>
    <router-link to='/about' active-class="active"></router-link>
    ...
    
    // 每一个都添加太麻烦了, 可以在router - index.js中统一修改
    const app = new VueRouter({
        router,
        mode: 'history',
        linkActiveClass: 'active'
    })
    

    路由跳转第二种方法

    在路由跳转需要执行对应的js代码的时候, 可以使用

    -- App.vue文件
    
    <div>
        <router-link @click="linkToHome"></router-link>
        <router-link @click="linkToAbout></router-link>
        <router-view></router-view>
    </div>
    
    <script>
    export default {
        name: 'App',
        methods: {
            // Vue-router在所有组件里都加入了$router 
            linkToHome () {
                // push => pushState 可以返回
                this.$router.push('./home')
            },
            linkToAbout () {
                // replace => replaceState 没有返回
                this.$router.replace('./about')
            }
        }
    }
    </script>
    

    动态路由

    某些情况下,一个页面的path路径可能是不确定的,比如我们进入用户界面时,希望是如下的路径:

    • /user/aaaa或/user/bbbb
    • 除了有前面的/user之外,后面还跟上了用户的ID
    • 这种path和Component的匹配关系,我们称之为动态路由(也是路由传递数据的一种方式)
    -- router - index.js文件 
    
    import Vue from ‘vue’
    import VueRouter from ‘vue-router’
    import Home from '../components/Home'
    import About from '../components/About'
    import User from '../components/User'
    
    Vue.use(VueRouter)
    
    const routes = [
        {
            path: '/', 
            redirect: './home'
        },
        {
            path: '/home',
            component: Home
        },
        {
            path: '/about',
            component: About
        },
        {
            path: '/user/:userId',
            component: User
        }
    ]
    
    const app = new VueRouter({
        router,
        mode: 'history'
    })
    
    export default router
    
    -- App.vue文件
    
    <div>
        <router-link to="/home"></router-link>
        <router-link to="/about"></router-link>
        <router-link v-bind:to="'/user/' + userId"></router-link>
        <router-view></router-view>
    </div>
    
    <script>
    export default {
        name: 'App',
        data () {
            return: {
                userId: '球球'
            }
        }
    }
    </script>
    
    • 点击进入User页面后, 想要拿到User的后缀(userId)
      this.$route.params
      $route: 谁处于活跃状态, 拿到谁
    上面的代码照写  
    
    -- User.vue文件  
    <template>
        <div>
            <h2>{{userId}}</h2>
        </div>
    </template>
    
    <script>
    export default {
        name: 'User',
        computed: {
            userId () {
                return this.$route.params.userId
            }
        }
    }
    </script>
    
    第二种写法  
    
    <template>
        <div>
            <h2>{{$route.params.userId}}</h2>
        </div>
    </template>
    
    <script>
    export default {
        name: 'User'
    }
    </script>
    

    路由打包文件解析

    https://www.bilibili.com/video/av59594689?p=108

    -- dist - static - js文件夹
    
    app.xxxxxx  -  当前应用程序开发的所有代码 (业务代码)  
    manifest.xxxxx  -  为了打包的代码做底层支撑 
    vendor.xxxx  -  提供第三方vue/vue-router/axios/bs
    
    

    路由懒加载

    懒加载的方式

    • 方式一: 结合Vue的异步组件和Webpack的代码分析
    const Home = resolve => {  
        require.ensure(['../components/Home.vue'], () => {
            resolve(require('../components/Home.vue'))
        })
    };          
    
    不推荐使用, 最早期的写法  
    
    • 方式二: AMD写法
    const About = resolve => require(['../components/About.vue'], resolve);
    
    • 方式三: 在ES6中, 我们可以有更加简单的写法来组织Vue异步组件和Webpack的代码分割.
    const Home = () => import('../components/Home.vue')
    
    最新的, 推荐  
    

    上面的案例增加路由软加载

    -- router - index.js文件 
    
    import Vue from ‘vue’
    import VueRouter from ‘vue-router’
    
    const Home = () => import('../components/Home.vue')
    const About = () => import('../components/About.vue')
    
    
    Vue.use(VueRouter)
    
    const routes = [
        {
            path: '/', 
            redirect: './home'
        },
        {
            path: '/home',
            component: Home
        },
        {
            path: '/about',
            component: About
        }
    ]
    
    const app = new VueRouter({
        router,
        mode: 'history'
    })
    
    export default router
    

    vue-router嵌套路由

    嵌套路由是一个很常见的功能
    比如在home页面中, 我们希望通过/home/news和/home/message访问一些内容.
    一个路径映射一个组件, 访问这两个路径也会分别渲染两个组件.

    实现嵌套路由步骤

    1. 创建对应的子组件, 并且在路由映射中配置对应的子路由.
    -- router - index.js文件 
    
    import Vue from ‘vue’
    import VueRouter from ‘vue-router’
    
    const Home = () => import('../components/Home.vue')
    const HomeNews = () => import('../components/HomeNews.vue')
    const About = () => import('../components/About.vue')
    
    Vue.use(VueRouter)
    
    const routes = [
        {
            path: '/', 
            redirect: './home'
        },
        {
            path: '/home',
            component: Home,
            children: {
                {
                    path: 'news',
                    component: HomeNews
                }
            }
        },
        {
            path: '/about',
            component: About
        }
    ]
    
    const app = new VueRouter({
        router,
        mode: 'history'
    })
    
    export default router
    
    1. 在嵌套组件内部使用< router-view>标签.
    -- Home.vue文件
    
    <div>
        <h2>我是首页</h2>
        // 要写完整的路径
        <router-link to="/home/news"></router-link>
        <router-view></router-view>
    </div>
    
    <script>
    export default {
        name: 'App'
    }
    </script>
    

    vue-router参数传递

    准备工作
    为了演示传递参数, 我们这里再创建一个组件, 并且将其配置好

    • 第一步: 创建新的组件Profile.vue
    • 第二步: 配置路由映射
    • 第三步: 添加跳转的< router-link>

    传递参数的方式

    传递参数主要有两种类型: params和query

    params的类型

    1. 配置路由格式: /router/:id
    2. 传递的方式: 在path后面跟上对应的值
    3. 传递后形成的路径: /router/123, /router/ab
    就是动态路由的传递方式 ^^ 往上看
    

    query的类型

    1. 配置路由格式: /router, 也就是普通配置
    -- index.js文件 
    
    import Vue from ‘vue’
    import VueRouter from ‘vue-router’
    
    const Home = () => import('../components/Home.vue')
    const User = () => import('../components/User.vue')
    
    Vue.use(VueRouter)
    
    const routes = [
        {
            path: '/', 
            redirect: './home'
        },
        {
            path: '/user', 
            component: User
        }
    ]
    
    const app = new VueRouter({
        router,
        mode: 'history'
    })
    
    export default router
    
    1. 传递的方式: 对象中使用query的key作为传递方式
    -- User.vue文件
    
    <div>
        <router-link to="/home"></router-link>
        <router-link :to="{path: '/user', query: {name: 'qiuqiu', age: 18}}"></router-link>
        <router-view></router-view>
    </div>
    
    <script>
    export default {
        name: 'App',
        data () {
            return: {
                userId: '球球'
            }
        }
    }
    </script>
    
    1. 传递后形成的路径: /router?id=123, /router?id=abc
    <template>
        <div>
            <h2>{{$route.query}}</h2>
            <p>{{$route.query.name}}</p>
        </div>
    </template>
    
    <script>
    export default {
        name: 'User'
    }
    </script>
    

    通过点击事件带参数

    参考上面写的动态跳转的第二个方法

    -- index.js文件 
    
    import Vue from ‘vue’
    import VueRouter from ‘vue-router’
    
    const Home = () => import('../components/Home.vue')
    const User = () => import('../components/User.vue')
    
    Vue.use(VueRouter)
    
    const routes = [
        {
            path: '/', 
            redirect: './home'
        },
        {
            path: '/user', 
            component: User
        }
    ]
    
    const app = new VueRouter({
        router,
        mode: 'history'
    })
    
    export default router
    
    -- User.vue文件
    
    <div>
        <router-link to="/home"></router-link>
        <button #click="userClick"></button>
        <router-view></router-view>
    </div>
    
    <script>
    export default {
        name: 'App',
        methods: {
            userClick () {
                // 跳转 带参数
                this.$router.push({
                    path: '/user',
                    query: {
                        name: 'qiuqiu',
                        age: '18'
                    }
                })
            }
        }
    }
    </script>
    

    r o u t e 和 route和 routerouter的区别

    • $router为VueRouter实例,想要导航到不同URL,则使用router.push方法
    • $route为当前router(选中状态)跳转对象里面可以获取name、path、query、params等

    route的由来–看源码
    https://www.bilibili.com/video/av59594689?p=113

    vue-router导航守卫

    为什么使用导航守卫?
    我们来考虑一个需求: 在一个SPA应用中, 如何改变网页的标题呢?

    • 网页标题是通过来显示的, 但SPA只有一个固定的HTML, 切换不同页面时, 标题并不会改变
    • 可以通过JS来修改的内容 window.document.title = “新的标题”

    更好的办法-使用导航守卫
    vue-router提供的导航守卫主要用来监听监听路由的进入和离开的.
    vue-router提供了beforeEach和afterEach的钩子函数, 它们会在路由即将改变前和改变后触发.

    导航守卫使用
    我们可以利用beforeEach来完成标题的修改.
    首先, 我们可以在钩子当中定义一些标题, 可以利用meta来定义
    其次, 利用导航守卫,修改我们的标题.

    -- index.js文件 
    
    import Vue from ‘vue’
    import VueRouter from ‘vue-router’
    
    const Home = () => import('../components/Home.vue')
    const User = () => import('../components/User.vue')
    
    Vue.use(VueRouter)
    
    const routes = [
        {
            path: '/home', 
            redirect: './home',
            // 元数据 (描述数据的数据)
            meta: {
                title: '首页'
            }
        },
        {
            path: '/user', 
            component: User,
            meta: {
                title: '用户'
            }
        }
    ]
    
    const app = new VueRouter({
        router,
        mode: 'history'
    })
    
    // 前置守卫(guard)
    router.beforeEach((to, from, next) => {
        // 从from跳转到to
        window.document.title = to.matched[0].meta.title;
        next();
    })
    
    export default router
    

    导航钩子的三个参数解析:

    • to: 即将要进入的目标的路由对象
    • from: 当前导航即将要离开的路由对象
    • next: 调用该方法后, 才能进入下一个钩子

    导航守卫补充

    • 如果是后置钩子, 也就是afterEach, 不需要主动调用next()函数.
    • 上面我们使用的导航守卫, 被称之为全局守卫
      路由独享的守卫
      组件内的守卫
    // 后置守卫(guard)
    router.afterEach((to, from) => {
        
    })
    

    keep-alive

    keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染

    它们有两个非常重要的属性:

    • include - 字符串或正则表达,只有匹配的组件会被缓存
    • exclude - 字符串或正则表达式,任何匹配的组件都不会被缓存

    router-view 也是一个组件,如果直接被包在 keep-alive 里面,所有路径匹配到的视图组件都会被缓存

    <keep-alive>
        <router-view>
            // 所有路径匹配到的视图组件都会被缓存
        </router-view>
    </keep-alive>
    

    路径配置问题补充

    图片等静态资源引用路径,如果修改太麻烦了. 可以统一做配置.

    -- build 文件夹 - webpack.base.config.js文件  
    
    resolve: {
        extensions: ['.js', '.vue', '.json'],
        alias: {
            '@': resolve('src'),
            'assets': resolve('src/assets'),
            'components': resolve('src/components'),
            'views': resolve('src/views')
        }
    }
    
    使用: 
    import xxx from './components/xxx'  
    --> 
    import xxx from 'components/xxx'  
    
    <img src="../../assets/img/xxx.jpg">
    -->
    <img src="~assets/img/xxx.jpg">  
    在html增加需要加~号
    

    七. Vuex详解

    Vuex是做什么的

    Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式

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

    状态管理到底是什么

    • 状态管理模式、集中式存储管理
    • 可以简单的将其看成把需要多个组件共享的变量全部存储在一个对象里面
    • 然后将这个对象放在顶层的Vue实例中,让其他组件可以使用
    • 那么多个组件是不是就可以共享这个对象中的所有变量属性了呢

    管理什么状态呢
    如果你做过大型开放,你一定遇到过多个状态,在多个界面间的共享问题。
    比如用户的登录状态、用户名称、头像、地理位置信息等等。
    比如商品的收藏、购物车中的物品等等。
    这些状态信息,我们都可以放在统一的地方,对它进行保存和管理,而且它们还是响应式的

    Vuex状态管理

    单界面的状态管理

    State:状态。(你姑且可以当做就是data中的属性)
    View:视图层,可以针对State的变化,显示不同的信息。(这个好理解吧?)
    Actions:这里的Actions主要是用户的各种操作:点击、输入等等,会导致状态的改变

    官方看图 
    
    View->Actions->State->View
    

    多界面的状态管理

    官方看图 
    

    创建使用

    需求: 一个数字, 二个按钮, 加减

    1. 需要在某个地方存放我们的Vuex代码, 先创建一个文件夹store,并且在其中创建一个index.js文件
      在index.js文件中写入如下代码
    import Vue from 'Vue'
    import Vuex from 'Vuex'
    
    Vue.use(Vuex)  
    
    const store = new Vuex.store({
        state: {
            count: 0
        },
        mutations: {
            // state是默认的参数 
            increment(state) {
                state.count++
            },
            decrement(state) {
                state.count--
            }
        }
    })
    
    export default store
    
    1. 挂载到Vue实例中, 让所有的Vue组件都可以使用这个store对象 - 来到main.js文件,导入store对象,并且放在new Vue中, 这样在其他Vue组件中,我们就可以通过this.$store的方式,获取到这个store对象了
    import Vue from 'vue'
    import App from './App'
    impotr store from './store'
    
    new Vue({
        e;: '#app',
        stroe,
        render: h => h(App)
    })
    
    1. 使用Vuex的count
    <template>
        <dov id="app">
            <p>{{count}}</p>
            <button @click="increment">+1</button>
            <button @click="decrement">-1</button>
        </div>
    </template>
    
    <script>
    export default {
        name: 'App',
        computed: {
            count: function () {
                return this.$store.state.count
            }
        },
        methods: {
            increment: function 90 {
                this.$store.commit('increment');
            },
            decrement: function 90 {
                this.$store.commit('decrement');
            }
        }
    }
    </script>
    
    1. 总结
    • 通过this.$store.state.属性的方式来访问状态
    • 通过this.$store.commit(‘mutation中方法’)来修改状态

    Vuex核心概念

    state单一状态树

    Vuex提出使用单一状态树, 即单一数据源

    应用开发中

    • 如果你的状态信息是保存到多个Store对象中的,那么之后的管理和维护等等都会变得特别困难。
    • 所以Vuex也使用了单一状态树来管理应用层级的全部状态。
    • 单一状态树能够让我们最直接的方式找到某个状态的片段,而且在之后的维护和调试过程中,也可以非常方便的管理和维护

    Getters

    类似于computed
    需要从store中获取一些state变异后的状态

    案例: 获取学生年龄大于20的个数

    const store = new Vuex.store({
        state: {
            students: [
                {id: 100, name: 'aaa', age: 10},
                {id: 100, name: 'bbb', age: 12},
                {id: 100, name: 'ccc', age: 11},
                {id: 100, name: 'ddd', age: 12},
            ]
        },
        getters: {
            // state是默认参数
            getAgesCount (state) {
                return state.students.filter(s => s.age > 10)
            }
        }
    })
    
    • computed的写法
    computed: {
        getAgesCount () {
            return this.$store.state.students.filter(s => s.age > 10)
        }
    }
    

    Getters作为参数和传递参数
    需求; 如果我们已经有了一个获取所有年龄大于20岁学生列表的getters, 那么代码可以这样来写
    需求2: getters默认是不能传递参数的, 如果希望传递参数, 那么只能让getters本身返回另一个函数.
    比如上面的案例中,我们希望根据ID获取用户的信息

    const store = new Vuex.store({
        state: {
            students: [
                {id: 100, name: 'aaa', age: 10},
                {id: 100, name: 'bbb', age: 12},
                {id: 100, name: 'ccc', age: 11},
                {id: 100, name: 'ddd', age: 12},
            ]
        },
        getters: {
            getAgesCount (state) {
                return state.students.filter(s => s.age > 10)
            },
            // 需求1
            getAgesCountLength(state, getters) {
                return getters.getAgesCount.length
            },
            // 需求2
            // 在调用的时候 传入了参数
            getAges(state) {
                return function (age) {
                    renturn stage.students.filter(s => s.id === id)
                }
            }
        }
    })
    

    Mutation

    Mutation状态更新

    Vuex的store状态的更新唯一方式:提交Mutation

    Mutation主要包括两部分

    • 字符串的事件类型(type)
    • 一个回调函数(handler),该回调函数的第一个参数就是state

    Mutation定义方式

    mutations: {
        aaa(state) {
            state.xx++
        }
    }
    

    通过Mutation更新

    bbb: function () {
        this.$store.commit('aaa')
    }
    

    Mutation传递参数

    1. Mutation提交风格一
      在通过mutation更新数据的时候, 有可能我们希望携带一些额外的参数
      参数被称为是mutation的载荷(Payload)
    aaa(state, n) {
        state.count -= n;
    }
    
    bbb: function () {
        this.$store.commit('aaa', 1);
    }
    

    有很多参数需要传递.
    通常会以对象的形式传递, 也就是payload是一个对象

    aaa(state, payload) {
        state.count -= payload.count;
    }
    
    bbb: function () {
        this.$store.commit('aaa', {count: 0});
    }
    
    1. Mutation提交风格二
      通过commit进行提交是一种普通的方式
      Vue还提供了另外一种风格, 它是一个包含type属性的对象
    this.$store.commit({
        type: 'aaa',
        count: 100
    })
    

    Mutation响应规则

    Vuex的store中的state是响应式的, 当state中的数据发生改变时, Vue组件会自动更新.

    必须遵守一些Vuex对应的规则:

    1. 提前在store中初始化好所需的属性.
    2. 当给state中的对象添加新属性时, 使用下面的方式:
      • 方式一: 使用Vue.set(obj, ‘newProp’, 123)
      • 方式二: 用心对象给旧对象重新赋值
    // 以下的方式, 修改了数据, 但是界面未更新, 不是响应式 
    const store = new Vuex.store({
        state: {
            info: {
                nameL: 'qiuqiu',
                age: 18
            }
        },
        mutations: {
            upInfo(state, payload) {
                state.info('sex') = payload.sex;
            }
        }
    })
    
    // 需要数据实时更新界面的写法 
    mutations: {
        upInfo(state, payload) {
            // 方法一: Vue.set()
            Vue.set(state.info, 'sex', payload.sex);
            // 方法二: 给info赋值一个新的对象  
            state.info = {...state.info, 'sex': payload,sex};
        }
    }
    
    // 属性删除  Vue.delete(obj, 'newProp')
    

    Mutation常量类型 – 概念

    考虑下面的问题

    • 在mutation中, 我们定义了很多事件类型(也就是其中的方法名称).
      当我们的项目增大时, Vuex管理的状态越来越多, 需要更新状态的情况越来越多, 那么意味着Mutation中的方法越来越多.
    • 方法过多, 使用者需要花费大量的经历去记住这些方法, 甚至是多个文件间来回切换, 查看方法名称, 甚至如果不是复制的时候, 可能还会出现写错的情况.

    如何避免上述的问题
    在各种Flux实现中, 一种很常见的方案就是使用常量替代Mutation事件的类型.
    我们可以将这些常量放在一个单独的文件中, 方便管理以及让整个app所有的事件类型一目了然.

    具体怎么做
    我们可以创建一个文件: mutation-types.js, 并且在其中定义我们的常量.
    定义常量时, 我们可以使用ES2015中的风格, 使用一个常量来作为函数的名称.

    -- mutation-types.js 
    export const UPDATE_INFO = 'UPDATE_INFO'
    
    -- store - index.js
    import * as type from './mutation-types'
    
    Vue.use(Vuex)
    
    const store = new Vuxe.Store({
        state: {
            info: {
                name: 'qiuqiu', age: 18
            }
        },
        mutations: {
            [UPDATE_INFO] (state, payload) {
                state.info = {...state.info, 'sex': payload.sex}
            }
        }
    })
    
    -- App.vue
    import {UPDATE_INFO} from './mutation-types'
    
    export default {
        name: 'App',
        methods: {
            this.$store.commit(UPDATE_INFO, {sex: '女'});
        }
    }
    

    Mutation同步函数

    使用谷歌插件–devtools

    Actions

    不要再Mutation中进行异步操作.
    Action类似于Mutation, 但是是用来代替Mutation进行异步操作的.

    Actions基本操作

    const store = new Vuex.Store({
        state: {
            count: 0
        },
        mutations: {
            addCount (state) {
                state.count++
            }
        },
        actions: {
            // 默认参数 context 上下文
            // 所用数据修改都要在mutations中, 所以在actions中异步后, 也要去mutations中修改 
            // 在mutations中修改, 才能被devtools监控到
            addCount (context) {
                context.commit('addCount');
            }
        }
    })
    
    methods: {
        addCount () {
            // 调用actions中的方法, 使用的是 dispatch
            this.$store.dispatch('addCount');
        })
    }
    

    Action返回的Promise

    actions: {
        addCount (context) {
            return new Promise((resolve) => {
                setTimout (() => {
                    context.commit('addCount');
                    resolve();
                }, 1000)
            })
        }
    }
    
    
    methods: {
        addCount () {
            this.$store.dispatch('addCount').then(res => {
                console.log('完成了接口的操作')
            })
        }
    }
    

    Module

    Vue使用单一状态树,那么也意味着很多状态都会交给Vuex来管理.
    当应用变得非常复杂时,store对象就有可能变得相当臃肿.
    为了解决这个问题, Vuex允许我们将store分割成模块(Module), 而每个模块拥有自己的state、mutation、action、getters等

    const moduleA = {
        state: {...},
        mutations: {...},
        actions: {...},
        getters: {...}
    }
    
    const moduleB = {
        state: {...},
        mutations: {...},
        actions: {...},
        getters: {...}
    }
    
    const store = new Vuex.Store({
        modules: {
            a: moduleA,
            b: moduleB
        }
    })
    
    store.state.a // mouduleA的state的状态
    store.state.b // mouduleB的state的状态
    

    Module局部状态
    上面的代码中, 我们已经有了整体的组织结构, 下面我们来看看具体的局部模块中的代码如何书写.
    我们在moduleA中添加state、mutations、getters
    mutation和getters接收的第一个参数是局部状态对象

    const moduleA = {
        state: {
            count: 0
        },
        mutations: {
            addCount (state) {
                state.count++;
            }
        },
        getters: {
            doubleCount (state) {
                return state.count * 2;
            }
        }
    }
    
    const moduleB = {
    }
    
    const store = new Vuex.Store({
        state: {
            gCount: 111
        },
        modules: {
            a: moduleA,
            b: moduleB
        }
    })
    
    <script>
        export default {
            name: 'App',
            computed: {
                count () {
                    return this.$store.getters.doubleCount;
                }
            },
            methods: {
                addCount () {
                    this.$store.commit('addCount');
                }
            }
        }
    </script>  
    
    
    注意:
    虽然, 我们的doubleCount和increment都是定义在对象内部的.
    但是在调用的时候, 依然是通过this.$store来直接调用的.
    

    Actions的写法
    接收一个context参数对象
    局部状态通过 context.state 暴露出来,根节点状态则为 context.rootState

    const muduleA = {
        //...
        actions: {
            addCountSum ({state, commit, rootState}) {
                if ((state.count + rootState.count) / 2 === 1) {
                    commit('addCount');
                }
            }
        }
    }
    

    getters的写法

    const moduleA = {
        // ...
        getters: {
            sunRootCount (state, getters, rootState) {
                return state.count + rootState.count;
            }
        }
    }
    

    项目结构

    components文件夹  
    store文件夹
      - index.js        -> 组装模块并导出 store的文件
      - actions.js      -> 根级别的 actions
      - mutations.js    -> 根级别的 mutations
      - modules            
          - aaaa.js     -> a模块  
          - bbbb.js     -> b模块 
    

    八. 网络封装

    网络模块的选择

    选择一: 传统的Ajax是基于XMLHttpRequest(XHR)
    为什么不用它呢?
    非常好解释, 配置和调用方式等非常混乱.
    编码起来看起来就非常蛋疼.
    所以真实开发中很少直接使用, 而是使用jQuery-Ajax

    选择二: 在前面的学习中, 我们经常会使用jQuery-Ajax
    相对于传统的Ajax非常好用.
    为什么不选择它呢?
    首先, 我们先明确一点: 在Vue的整个开发中都是不需要使用jQuery了.
    那么, 就意味着为了方便我们进行一个网络请求, 特意引用一个jQuery, 你觉得合理吗?
    jQuery的代码1w+行.
    Vue的代码才1w+行.
    完全没有必要为了用网络请求就引用这个重量级的框架.

    选择三: 官方在Vue1.x的时候, 推出了Vue-resource.不在维护了
    Vue-resource的体积相对于jQuery小很多.
    另外Vue-resource是官方推出的.
    为什么不选择它呢?
    在Vue2.0退出后, Vue作者就在GitHub的Issues中说明了去掉vue-resource, 并且以后也不会再更新.
    那么意味着以后vue-reource不再支持新的版本时, 也不会再继续更新和维护.
    对以后的项目开发和维护都存在很大的隐患.

    选择四: 作者推荐了框架axios
    优点:

    1. 在浏览器中发送XMLHttpRequests请求
    2. 在node.js中发送http请求
    3. 支持Promise API
    4. 拦截请求和响应
    5. 转换请求和相应数据
    6. 等等

    jsonp

    在前端开发中, 我们一种常见的网络请求方式就是JSONP
    使用JSONP最主要的原因往往是为了解决跨域访问的问题.
    JSONP的原理是什么呢?
    JSONP的核心在于通过script标签的src来帮助我们请求数据.
    原因是我们的项目部署在domain1.com服务器上时, 是不能直接访问domain2.com服务器上的资料的.
    这个时候, 我们利用script标签的src帮助我们去服务器请求到数据, 将数据当做一个javascript的函数来执行, 并且执行的过程中传入我们需要的json.
    所以, 封装jsonp的核心就在于我们监听window上的jsonp进行回调时的名称.

    JSONP如何封装呢
    我们一起自己来封装一个处理JSONP的代码吧.

    let count = 1;
    export default function originPJSONP (option) {
        // 1. 从传入的option中提取URL
        const url = option.url;
        
        // 2. 在body中添加script标签  
        const body = document.getElementsByTagName('body')[0];
        const script = document.createElement('script);
        
        // 3. 内部生成一个不重复的callback
        const callback = 'jsonp' + count++
        
        // 4. 监听window上的jsonp调用 
        return new Promise((resolve, reject) => {
            try {
                window[callback] = function (result) {
                    body.removeChild(script);
                    resolve(result);
                }
                const params = handleParam(option.data);
                script.src = url + '?callback=' + callback + params;
                body.appendChild(script);
            } catch (e) {
                body.removeChild(script);
                reject(e);
            } 
        }) 
    }
    
    funciton handleParam (data) {
        let url = '';
        for (let key in data) {
            let value = data[key] !== undefinded ? data[key] : 
            url += '&${key}=${encodeURIComponent(value)}'
        }
        return url
    }
    

    axios使用

    axios请求方式

    支持多种请求方式:

    • axios(config)
    • axios.request(config)
    • axios.get(url, [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发送并请求

    同时发送两个请求

    使用axios.all, 可以放入多个请求的数组.
    axios.all([]) 返回的结果是一个数组,使用 axios.spread 可将数组 [res1,res2] 展开为 res1, res2

    import axios from 'axios'
    
    export default {
        name: 'app',
        created () {
            // 发送并发请求  
            axios.all([axios.get('tp://000.000/xxxx'), 
                axios.get('tp://000.000/xxxx', {params: {type: 'sell', page: 1}})])
            .then(axios.spread((rea1, res2) => {
                console.log(res1);
                console.log(res2);
            }))    
        }
    }
    

    全局配置

    开发中,很多参数是固定的. 可以进行一些抽取, 也可以利用axiox的全局配置

    axios.defaults.baseURL = 'tp://000.000'
    axios.defaults.headers.post['Content-Type'] = 'xxxxx'
    
    
    export default {
        name: 'app',
        created () {
            // 提取全局的配置
            axios.defaults.baseURL = 'http://000.000'
            
            // 发送并发请求  
            axios.all([
                axios.get('/xxxx'), 
                axios.get('/xxxxx', {
                    params: {
                        type: 'sell', 
                        page: 1
                    }}
                )
            ]).then(axios.spread((rea1, res2) => {
                console.log(res1);
                console.log(res2);
            }))    
        }
    }
    

    常见的配置选项

    请求地址   url: ‘/user’,
    请求类型   method: ‘get’,
    请根路径   baseURL: ‘http://www.mt.com/api’,
    请求前的数据处理   transformRequest:[function(data){}],
    请求后的数据处理   transformResponse: [function(data){}],
    自定义的请求头   headers:{‘x-Requested-With’:‘XMLHttpRequest’},
    URL查询对象   params:{ id: 12 },
    查询对象序列化函数 
    paramsSerializer: function(params){ }
    request body
    data: { key: ‘aa’},
    超时设置s   timeout: 1000,
    跨域是否带Token   withCredentials: false,
    自定义请求处理   adapter: function(resolve, reject, config){},
    身份验证信息   auth: { uname: ‘’, pwd: ‘12’},
    响应的数据格式 json / blob /document /arraybuffer / text / stream
    responseType: ‘json’,
    

    axios实例

    为什么要创建axios的实例

    • 当我们从axios模块中导入对象时, 使用的实例是默认的实例.
    • 当给该实例设置一些默认配置时, 这些配置就被固定下来了.
    • 但是后续开发中, 某些配置可能会不太一样.
    • 比如某些请求需要使用特定的baseURL或者timeout或者content-Type等.
    • 这个时候, 我们就可以创建新的实例, 并且传入属于该实例的配置信息.
    const inatancel = axios.create({
        baseURL: 'http: //111.2222',
        timeout: 5000
    })
    
    instancel({
        url: '/home/mutidata'
    }).then(res => {
        console.log(res);
    })
    
    instancel({
        url: '/home/index'
    }).then(res => {
        console.log(res);
    })
    
    
    const other = axios.create({
        baseURL: 'http: //111.2222',
        timeout: 5000,
        // header: {}
    })
    

    axios封装

    https://www.bilibili.com/video/av59594689?p=145
    简化的比较复杂 看视频吧

    1. 建立一个network文件夹, 在里面建立一个request.js文件
    export function request (config) {
        // 1. 创建axios的实例 
        const instance = axios.create({
            baseURL: 'http://111.222.33.44',
            timeout: 5000
        })
        
        // 2. 发送真正的网络请求
        // 返回的 instancel(config)就是一个promise
        return instancel(config)
    }
    
    1. 在使用的文件中. 封装request模块
    import {request} from './network/request';
    
    request({
        url: '/home/multidata'
    }).then(res => {
        console.log(res);
    }).catch(err => {
        console.log(err);
    })
    

    axios拦截器

    axios提供了拦截器,用于我们在发送每次请求或者得到相应后,进行对应的处理

    export function request (config) {
        // 1. 创建axios的实例 
        const instance = axios.create({
            baseURL: 'http://111.222.33.44',
            timeout: 5000
        })
        
        // 2. axios拦截器 
        // config可以随便命名
        instance.interceptiors.request.use(config => {
            // 不返回, 调用的时候会进入err
            return config
        }, err => {      // 发送都没成功
            console.log(err);
        })
        
        // 3. 发送真正的网络请求
        return instancel(config)
    }
    

    拦截器中做什么

    • 请求成功
    1. 过滤一些信息
    2. 每次请求后, 会展示loding图标
    3. 某些网络请求(登录的token), 必须携带某一些特殊的信息
    • 响应拦截
    instance.interceptors.resques.use(res => {
        return config
    }, err => {
        console.log(err);
    })
    

    九.项目实战

    十. 补充知识点

    ES6

    块级作用域

    变量作用域: 交叠在什么范围内是可用

    ES5之前因为if和for都没有块级作用域的概念, 在很多时候, 我们必须借助于function的作用域来解决应用外变量的问题
    ES6中, 加入了let.let它有if和for的块级作用域

    增强写法

    属性的增强写法

    // ES5  对象字面量  
    const name = 'why';
    const age = 18;
    const obj = {
        name: name,
        age: age
    }
    
    // ES6
    const obj = {
        name,
        age
    }
    

    函数的增强写法

    // ES5 
    const obj = {
        run: function () {
            
        }
    }
    
    // ES6
    const obj = {
        run() {
            
        }
    }
    

    JS高阶函数

    编程范式: 声明式编程 or 命令式编程

    // vue 编程范式: 声明式编程
    <div id='app'>{{message}}</div>
    
    const app = new Vue({
        el: 'app', // 用户挂载要管理的元素
        data: { // 定义数据
            message: '你好啊'
        }
    })
    
    // 元素JS的做法: 命令式编程 
    1. 创建div元素, 设置id属性
    2. 定义一个变量叫message
    3. 将message变量放在前面的div元素中显示
    

    编程范式: 面向对象编程 or 函数式编程

    面向对象编程 (第一公民: 对象) or 函数式编程 (第一公民: 函数)

    https://www.bilibili.com/video/av59594689?p=44

    高阶函数 filter / map / reduce

    案例详细讲解:

    1. 一般的JS写法
      缺点: 一步一步, 代码太多
    const nums = [10, 20, 111, 222, 444, 40, 50];
    
    // 需求一: 取出所有小于的数字
    let newNums = [];
    for (let n of nums) {
        if (n < 100) {
            newNums.push(n);
        }
    }
    
    // 需求二: 将所有小于100的数字进行转化 - 全部 *2
    let newNums2 = [];
    for (let n of newNums) {
        newNums2.push(n * 2);
    }
    
    // 需求三: 将所有newNums2的数字相加, 得到最终的结果
    let total = 0; 
    for (let n of newNums2) {
        total += n;
    }
    
    1. 单个需求, 高阶函数写法

    filter - 回调函数有一个要求: 必须返回一个boolean值
    true: 返回true时, 函数内部会自动将这次回调的n加入到新的数组中
    false: 返回false时, 函数内部会过滤掉这次的n

    const nums = [10, 20, 111, 222, 444, 40, 50];
    
    // 需求一: filter
    let newNums = nums.filter(functcion (n) {
        return n < 100;
    })
    
    // 遍历出数组中的每一个是否<100来判断true / false, 然后为true加入新数组newNums中
    
    

    map

    // 需求二: map
    let newNums2 = newNums.map(functcion (n) {
        return n * 2;
    })
    
    

    reduce
    作用: 对数组中所有的内容进行汇总

    // reduce原理解释
    let total = newNums2.reduce(function (preValue, n) {
        return 100;
    }, 0);
    
    // reduce (参数一, 参数二) 参数二是第一个preValue值
    // 第一次: preValue 0   n  20   
    // 第二次: preValue 100   n  40
    // 第三次: preValue 100   n  80
    // 第四次: preValue 100   n  100
    
    // 需求三: reduce
    let total = newNums2.reduce(function (preValue, n) {
        return preValue + n;
    }, 0);
    
    // 第一次: preValue 0   n  20   
    // 第二次: preValue 20   n  40
    // 第三次: preValue 60   n  80
    // 第四次: preValue 140   n  100
    
    1. 高阶函数-链式连接的写法
    const nums = [10, 20, 111, 222, 444, 40, 50];
    
    let total = nums.filter(function (n) {
        return n < 100;
    }).map(function (n) {
        return n * 2;
    }).reduce(function (preValue, n) {
        return preValue + n;
    }, 0);
    
    // 240
    

    Promise

    ES6中一个非常重要和好用的特性就是Promise

    Promise做什么的
    Promise是异步编程的一种解决方案

    Promise什么时候用来处理异步事件
    常见的场景: 一般在有异步操作-比如网络请求时

    Promise的基本使用

    调接口的时候用Promise封装一遍, 成功的时候走then, 失败走catch函数里的代码

    需求: 以定时器为例
    setTimeout(() => {
        console.log('哈哈哈');
    }, 1000) 
    
    • 第一种写法
    // 参数-> 函数  
    // resolve, reject本身也是函数
    // 成功的时候
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve();
        }, 1000) 
    }).then(() => {
        console.log('哈哈哈');
    }) 
    
    // 失败的时候
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve();
            reject('error message');
        }, 1000) 
    }).then(() => {
        console.log('哈哈哈');
    }).catch(error => {
        console.log(error);
    })
    
    • 第二种写法
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(data);
            reject('error message');
        }, 1000) 
    }).then(data => {
        console.log(data);
    }, err => {
        console.log(err);  
    })
    

    定时器异步事件解析

    1. new Promise很明显是创建一个Promise对象
    2. 小括号中((resolve, reject) => {})也很明显就是一个函数,而且我们这里用的是之前刚刚学习过的箭头函数
    • 但是resolve, reject它们是什么呢?
    • 我们先知道一个事实:在创建Promise时,传入的这个箭头函数是固定的(一般我们都会这样写)
    • resolve和reject它们两个也是函数,通常情况下,我们会根据请求数据的成功和失败来决定调用哪一个
    1. 成功还是失败?
    • 如果是成功的,那么通常我们会调用resolve(messsage),这个时候,我们后续的then会被回调
    • 如果是失败的,那么通常我们会调用reject(error),这个时候,我们后续的catch会被回调

    需求:

    1. 网络请求: 等到数据aaa -> 自己处理 (10行)
    2. 处理: aaa111 -> 自己处理(10行)
    3. 处理: aaa111222 -> 自己处理
    • 链式链接的写法
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('aaa');
        }, 1000)
    }).then(res => {
        // 1. 自己处理10行代码 
        console.log(res, '第一层的10行处理代码');
        
        // 2. 对结果进行第一次的处理
        return new Promise(resolve => {
            resolve(res + '111');
        })
    }).then(res => {
        console.log(res, '第二层的10行处理代码');
        
        return new Promise(resolve => {
            resolve(res + '222');
        })
    }).then(res +> {
        console.log(res, '第三层的处理代码')
    })
    
    • 链式链接的简写
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('aaa');
        }, 1000)
    }).then(res => {
        console.log(res, '第一层的10行处理代码');
        
        // 简写
        return Promise.resolve(res+ '111');
    }).then(res => {
        console.log(res, '第二层的10行处理代码');
        
        return Promise.resolve(res+ '222');
    }).then(res +> {
        console.log(res, '第三层的处理代码')
    })
    
    • 链式链接的简写2
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('aaa');
        }, 1000)
    }).then(res => {
        console.log(res, '第一层的10行处理代码');
        
        // 简写
        return res+ '111';
    }).then(res => {
        console.log(res, '第二层的10行处理代码');
        
        return res+ '222';
    }).then(res +> {
        console.log(res, '第三层的处理代码')
    })
    

    **需求: **

    1. 调用第一个接口, 等到数据aaa
    2. 调用第二个接口, 得到数据bbb
    3. 需要aaa bbb都有了在操作
    Promise.all({
        new Promise((resolve, reject) => {
            $ajax({
                url: '',
                success: function (data) {
                    resolve(data);
                }
            }),
            $ajax({
                url: '',
                success: function (data) {
                    resolve(data);
                }
            })
        }).then(results => {
            console.log(results[0]);  // aaa
            console.log(results[1]);  // bbb
            ...
        })
    }) 
    

    Promise三种状态

    异步操作后会有三种状态

    • pending:等待状态,比如正在进行网络请求,或者定时器没有到时间。
    • fulfill:满足状态,当我们主动回调了resolve时,就处于该状态,并且会回调.then()
    • reject:拒绝状态,当我们主动回调了reject时,就处于该状态,并且会回调.catch()
    展开全文
  • Vue学习路线图

    千次阅读 2018-12-11 22:50:02
    如果你是一名 Vue 开发新手,可能已经听过很多行话术语,比如单页面应用程序、异步组件、服务器端渲染等等,或者还听说过与 Vue 有关的一些工具和库,比如 Vuex、Webpack、Vue CLI 和 Nuxt。 那么究竟什么是Vue,有...

    Vue 概述

    如果你是一名 Vue 开发新手,可能已经听过很多行话术语,比如单页面应用程序、异步组件、服务器端渲染等等,或者还听说过与 Vue 有关的一些工具和库,比如 Vuex、Webpack、Vue CLI 和 Nuxt。

    那么究竟什么是Vue,有什么作用?

    Vue.js是一套用于构建用户界面的渐进式框架,主要用于快速的构建前端界面,与其它大型的前端框架不同,Vue被设计为可以自底向上逐层应用。

    相比Angular.js来说,Vue的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合,是初创项目的前端首选框架。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue也完全能够为复杂的单页应用提供驱动。

    单独来说,Vue.js是一个用于构建用户界面的前端库,本身就具有响应式编程和组件化的诸多优点。所谓响应式编程,即是一种面向数据流和变化传播的编程范式,可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。

    响应式编程在前端开发中得到了大量的应用,在大多数前端MVX框架都可以看到它的影子。相比较于Angular.js和React.js而言,Vue.js并没有引入太多的新概念,只是对已有的概念进行了精简。并且,Vue.js很好的借鉴了React.js的组件化思想,使应用开发起来更加容易,真正实现了模块化开发的目的。

    相比于Angular.js和React.js而言,Vue.js一直以轻量级、易上手而被人称道。MVVM的开发模式也使前端从传统的DOM操作中释放出来,开发者不需要再把时间浪费在视图和数据的维护上,只需要关注data的变化即可。并且,Vue的渲染层基于轻量级的virtual-DOM实现,在大多数的场景下初始化速度和内存消耗都提高2-4倍。同时,越来越多的移动客户端也开始支持使用Vue.js来进行开发,可以坚信使用Vue.js打造三端一致的Native应用将变成可能。

    作为一个新兴的前端框架,Vue.js大量借鉴和参考了Angular.js和React.js等优秀的前端框架。而在版本支持上,Vue.js抛弃了对IE8的支持,对移动端的支持也有一定的要求,也即是说使用Vue.js进行移动跨平台开发时需要Android 4.2+和iOS 7+支持。

    如果读者所在的项目是一个前后端分离的项目,亦或者是一个创业项目,想使用Vue.js打造三端一致的Native体验,那么Vue.js将是一个不错的选择。

    Vue 线路图

    俗话说,一口气吃不成胖子。对于一个新手,又如何学习Vue呢?还需要如何进阶呢?又需要掌握哪些高级知识?为了解答这些疑问,下面看一张Vue的技能图。
    在这里插入图片描述

    JavaScript 与Web基础

    Vue 作为一个用于构建 Web 用户界面的 JavaScript 框架,在开始使用 Vue 之前,你必须了解 JavaScript 和 Web 开发的基础知识。

    并且还需要掌握一些 Vue.js 生态系统的核心知识,包括 Vue 核心库、Vue Router 和 Vuex。

    Vue 核心功能

    从根本上说,Vue 就是一个用于同步网页的JavaScript技术框架。实现这一目标的关键特性是反应式(reactive)数据,以及指令和插值等模板功能。

    要构建一个 Vue 应用程序,你还需要知道如何在网页中安装 Vue,并了解 Vue 实例的生命周期等知识。

    组件

    其次,Vue 组件是独立的可重用 UI 元素。因此,你需要了解如何声明组件,以及如何通过 prop 和 event 在它们之间发生交互。

    了解如何组合组件也很重要,因为这对使用 Vue 构建健壮、可伸缩的应用程序来说至关重要。

    单页面应用程序

    单页面应用程序(SPA)架构通过单个网页实现传统多页面网站一样的功能,而且不会在每次用户触发导航时重新加载和重建页面。

    在将“页面”构建为 Vue 组件之后,就可以使用 Vue Router 将每个“页面”映射到一个唯一的路径,Vue Router 是一个用于构建 SPA 的工具,由 Vue 团队维护。

    状态管理

    随着应用程序变得越来越大,项目变得越来越复杂,SPA 页面中会有很多组件,管理全局状态变得异常困难,而且随着 prop 和 event 监听器的增加,组件变得越来越臃肿。

    这时候,一种称为Flux的特殊模式就出现了,它可以将数据保存在可预测且稳定的中央存储中。由 Vue 团队维护的 Vuex 库可以帮助你在 Vue.js 应用程序中实现 Flux。

    实战中的Vue

    脚手架

    如果你经常构建 Vue 应用程序,你会发现几乎每个项目都需要提供配置、设置和开发者工具。

    Vue 团队维护了一个叫作 Vue CLI 的工具,让你可以在几分钟内启动一个强大的 Vue 开发环境。

    全栈应用程序

    在实际开发中,真实的 Vue 应用程序通常是由数据来驱动用户界面渲染的。数据通常来自使用 Node、Laravel、Rails、Django 或其他服务器框架开发的 API。

    这些数据可能是由传统的 REST API 或 GraphQL 提供的数据,也可能是通过 Web 套接字提供的实时数据。

    所以,你还需要了解将 Vue 集成到完整技术栈中常用设计模式,以及确保 Vue 应用程序用户数据的安全性。

    测试

    如果你想开发出可维护且稳定的 Vue 应用程序,你还需要对它们进行测试(如单元测试、快照测试、黑盒测试等)。

    在 Vue 应用程序中,可以通过单元测试来确保你的组件能够为给定输入(即 prop 或用户输入)提供相同的输出(即重新渲染的 HTML 或发出的事件)。

    Vue 团队维护了一个叫作 Vue Test Utils 的工具,用于测试单独的 Vue 组件。

    优化

    当你将应用程序部署到远程服务器并且用户通过慢连接访问它时,它与你在开发环境中测试的速度和效率是不一样的。

    为了优化 Vue 应用程序,我们可以采用各种技术,包括服务器端渲染,也就是在服务器端执行 Vue 应用程序,然后输出 HTML 页面并传给用户。其他优化手段还包括使用异步组件和渲染函数。

    开发工具

    俗话说,磨刀不误砍柴工,写代码也一样,掌握Vue开发相关的工具和脚手架,可以大大的提高开发效率。

    JavaScript 和 Babel

    要获得增强的 Vue 开发体验,并利用新的浏览器功能,你可以使用最新的 JavaScript 标准 ES2015 和 ES2016 或更高版本提供的功能来构建 Vue 应用程序。

    不过,如果你选择使用现代 JavaScript,就需要提供一种支持旧版浏览器的方法,否则你的产品可能无法为大多数用户提供服务。

    要实现这一目的,您可以使用 Babel。它的作用是在应用程序发布之前将你的现代功能“转换”(翻译和编译)为标准语法,如将ES6翻译为浏览器能够识别的ES5。

    WebPack

    Webpack 是模块捆绑器,如果你的代码跨越了不同模块(例如不同的 JavaScript 文件),Webpack 可以将这些零散的代码“构建”到浏览器可读的单个文件中。

    Webpack 还可以作为构建管道,你可以在构建代码之前对代码进行转换,例如使用 Babel、Sass 或 TypeScript,还可以使用一系列插件来优化你的应用程序。

    很多开发人员觉得 Webpack 难以掌握,配置起来也很麻烦,但如果没有它,将无法使用 Vue 的一些有用的功能(如单文件组件)。

    最近发布的 Vue CLI 3 提供了一种用于在 Vue 项目中抽象和自动配置 Webpack 的解决方案。

    这是否意味着你不需要学习 Webpack 了?当然不是,因为你仍然不可避免地需要进行定制或调试 Webpack 配置。

    TypeScript

    TypeScript 是 JavaScript 语言的超集,本质上向这个语言添加了可选的静态类型和基于类的面向对象编程。为我们提供了类型(如String、Boolean、Number 等),这样我们就可以编写健壮的代码,并尽早发现错误。

    将于 2019 年推出的Vue.js 3版本 ,将完全使用 TypeScript 编写。因此学习TypeScript显得很有必要。

    Vue 的框架

    构建在 Vue 之上的框架可以让你无需从头开始实现服务器端渲染,还可以创建自己的组件库以及定制很多其他常见的任务。

    Nuxt.js

    如果你想要构建一个高性能的 Vue 应用程序,就需要基于组件的路由、服务器端渲染、代码拆分和其他功能进行实习。

    Nuxt.js 作为一个基于 Vue.js 的轻量级应用框架,可用来创建服务端渲染 (SSR) 应用,也可充当静态站点引擎生成静态站点应用,具有优雅的代码结构分层和热加载等特性。

    Nuxt.js 通过各种社区插件提供了这些开箱即用的功能,以及更多的功能选项,如 PWA。

    Vuetify

    谷歌的 Material Design 是一个使用十分广泛的页面样式指南,用于构建漂亮的逻辑用户界面,并被用在谷歌的产品(如 Android 和 Web)当中。

    Vuetify 在一系列 Vue 组件中实现了 Material Design。因此,你可以使用 Material Design 布局和样式快速构建 Vue 应用程序,以及模态、警报、导航栏、分页等小部件。

    Vuetify支持SSR(服务端渲染),SPA(单页应用程序),PWA(渐进式web应用程序)和标准HTML页面。

    NativeScript-Vue

    Vue.js 是一个用于构建 Web 用户界面的库。如果你想将它用于原生移动界面,可以使用 NativeScript-Vue 框架。

    NativeScript 是一个用于在 iOS 和 Android 上使用原生用户界面组件构建应用程序的系统,而 NativeScript-Vue 是一个基于 NativeScript 的框架,提供了 Vue 的语法和组件的使用方式。

    其他

    插件开发

    如果要在项目中重用 Vue 功能或为 Vue 生态系统做贡献,可以将功能作为 Vue 插件来开发。

    动画

    如果你需要使用动画,那么你需要了解一下 Vue 的过渡系统,它也是 Vue 核心的一部分。你可以通过在向 DOM 添加元素或从 DOM 中删除元素时应用动画。

    你需要创建 CSS 类来定义所需的动画效果,无论是淡入淡出、更改颜色还是你喜欢的其他方式。当向 DOM 中添加元素或从 DOM 中删除元素时,Vue 会检测到这些变更,并在过渡期间添加或删除相应的 CSS 类。

    渐进式 Web 应用程序

    渐进式 Web 应用程序(PWA)就像普通的 Web 应用程序一样,只是加入了改进的用户体验。例如,PWA 可能包括脱机缓存、服务器端渲染、推送通知等。

    大多数 PWA 功能可以通过 Vue CLI 3 插件或使用 Nuxt.js 等框架添加到 Vue 应用程序中,但你仍然需要了解一些关键技术,包括 Web App Manifest 和 ServiceWorker。

    如果你对PWA有兴趣,那么推荐您查看一些PWA的官网介绍

    展开全文
  • Vue学习总结(全)

    千次阅读 多人点赞 2021-02-24 16:46:13
    Vue项目的创建安装 # 全局安装 vue-cli $ cnpm install --global vue-cli # 创建一个基于 webpack 模板的新项目 $ vue init webpack my-project # 这里需要进行一些配置,默认回车即可 This will install Vue 2.x ...

    Vue项目的创建安装

    # 全局安装 vue-cli
    $ cnpm install --global vue-cli
    # 创建一个基于 webpack 模板的新项目
    $ vue init webpack my-project
    # 这里需要进行一些配置,默认回车即可
    This will install Vue 2.x version of the template.
    
    For Vue 1.x use: vue init webpack#1.0 my-project
    
    ? Project name my-project
    ? Project description A Vue.js project
    ? Author runoob <test@runoob.com>
    ? Vue build standalone
    ? Use ESLint to lint your code? Yes
    ? Pick an ESLint preset Standard
    ? Setup unit tests with Karma + Mocha? Yes
    ? Setup e2e tests with Nightwatch? Yes
    
       vue-cli · Generated "my-project".
    
       To get started:
       
         cd my-project
         npm install
         npm run dev
       
       Documentation can be found at https://vuejs-templates.github.io/webpack
    

    进入项目,安装并运行:

    $ cd my-project
    $ cnpm install
    $ cnpm run dev
     DONE  Compiled successfully in 4388ms
    
    > Listening at http://localhost:8080
    

    Vue项目目录结构

    目录/文件说明
    build项目构建(webpack)相关代码
    config配置目录,包括端口号等。我们初学可以使用默认的。
    node_modulesnpm 加载的项目依赖模块
    src这里是我们要开发的目录,基本上要做的事情都在这个目录里。里面包含了几个目录及文件:assets: 放置一些图片,如logo等。components: 目录里面放了一个组件文件,可以不用。App.vue: 项目入口文件,我们也可以直接将组件写这里,而不使用 components 目录。main.js: 项目的核心文件。
    static静态资源目录,如图片、字体等。
    test初始测试目录,可删除
    .xxxx文件这些是一些配置文件,包括语法配置,git配置等。
    index.html首页入口文件,你可以添加一些 meta 信息或统计代码啥的。
    package.json项目配置文件。
    README.md项目的说明文档,markdown 格式

    Vue模块语句

    插值表达式
    • 数据绑定最常见的形式就是使用 {{…}}(双大括号)的文本插值:
    • 使用 v-html 指令用于输出 html 代码
    • HTML 属性中的值应使用 v-bind 指令。缩写为 :
    • v-on 指令,它用于监听 DOM 事件。 缩写为 @
    • v-model 指令来实现双向数据绑定
    修饰符

    修饰符是以半角句号 . 指明的特殊后缀,用于指出一个指令应该以特殊方式绑定,.stop阻止事件冒泡,.prevent阻止默认事件发生

    过滤器

    Vue.js 允许你自定义过滤器,被用作一些常见的文本格式化

    过滤器函数接受表达式的值作为第一个参数。

    以下实例对输入的字符串第一个字母转为大写:

    <div id="app">
      {{ message | capitalize }}
    </div>
        
    <script>
    new Vue({
      el: '#app',
      data: {
        message: 'runoob'
      },
      filters: {
        capitalize: function (value) {
          if (!value) return ''
          value = value.toString()
          return value.charAt(0).toUpperCase() + value.slice(1)
        }
      }
    })
    </script>
    

    条件判断

    <div id="app">
        <div v-if="type === 'A'">
          A
        </div>
        <div v-else-if="type === 'B'">
          B
        </div>
        <div v-else-if="type === 'C'">
          C
        </div>
        <div v-else>
          Not A/B/C
        </div>
        <h1 v-show="ok">Hello!</h1>
    </div>
        
    <script>
    new Vue({
      el: '#app',
      data: {
        type: 'C',
        ok: 'true'
      }
    })
    </script>
    

    我们也可以使用 v-show 指令来根据条件展示元素

    循环语句

    v-for 可以绑定数据到数组来渲染一个列表:

    <div id="app">
      <ol>
        <li v-for="site in sites">
          {{ site.name }}
        </li>
      </ol>
    </div>
     
    <script>
    new Vue({
      el: '#app',
      data: {
        sites: [
          { name: 'Runoob' },
          { name: 'Google' },
          { name: 'Taobao' }
        ]
      }
    })
    </script>
    

    v-for 可以通过一个对象的属性来迭代数据:

    <div id="app">
      <ul>
        <li v-for="value in object">
        {{ value }}
        </li>
      </ul>
    </div>
     
    <script>
    new Vue({
      el: '#app',
      data: {
        object: {
          name: '菜鸟教程',
          url: 'http://www.runoob.com',
          slogan: '学的不仅是技术,更是梦想!'
        }
      }
    })
    </script>
    

    提供第二个的参数为键名:

    <div id="app">
      <ul>
        <li v-for="(value, key) in object">
        {{ key }} : {{ value }}
        </li>
      </ul>
    </div>
    

    第三个参数为索引:

    <div id="app">
      <ul>
        <li v-for="(value, key, index) in object">
         {{ index }}. {{ key }} : {{ value }}
        </li>
      </ul>
    

    v-for 也可以循环整数:

    <div id="app">
      <ul>
        <li v-for="n in 10">
         {{ n }}
        </li>
      </ul>
    </div>
    

    计算属性

    • 计算属性关键词: computed。
    • 计算属性在处理一些复杂逻辑时是很有用的。
    <div id="app">
      <p>原始字符串: {{ message }}</p>
      <p>计算后反转字符串: {{ reversedMessage }}</p>
    </div>
     
    <script>
    var vm = new Vue({
      el: '#app',
      data: {
        message: 'Runoob!'
      },
      computed: {
        // 计算属性的 getter
        reversedMessage: function () {
          // `this` 指向 vm 实例
          return this.message.split('').reverse().join('')
        }
      }
    })
    </script>
    

    实例中声明了一个计算属性 reversedMessage 。

    提供的函数将用作属性 vm.reversedMessage 的 getter 。

    vm.reversedMessage 依赖于 vm.message,在 vm.message 发生改变时,vm.reversedMessage 也会更新。

    computed vs methods

    我们可以使用 methods 来替代 computed,效果上两个都是一样的,但是 computed 是基于它的依赖缓存,只有相关依赖发生改变时才会重新取值。而使用 methods ,在重新渲染的时候,函数总会重新调用执行

    methods: {
      reversedMessage2: function () {
        return this.message.split('').reverse().join('')
      }
    }
    

    可以说使用 computed 性能会更好,但是如果你不希望缓存,你可以使用 methods 属性。

    监听属性(watch)

    我们可以通过 watch 来响应数据的变化,以下实例通过使用 watch 实现计数器:

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    <script src="https://cdn.staticfile.org/vue/2.4.2/vue.min.js"></script>
    </head>
    <body>
    <div id = "app">
     <p style = "font-size:25px;">计数器: {{ counter }}</p>
     <button @click = "counter++" style = "font-size:25px;">点我</button>
    </div>
    <script type = "text/javascript">
     var vm = new Vue({
        el: '#app',
        data: {
           counter: 1
        }
     });
     vm.$watch('counter', function(nval, oval) {
        alert('计数器值的变化 :' + oval + ' 变为 ' + nval + '!');
     });
    </script>
    </body>
    </html>
    
    <!DOCTYPE html>
    <html>
    <head>
    	<meta charset="utf-8">
    	<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
    	<script src="https://cdn.staticfile.org/vue/2.4.2/vue.min.js"></script>
    </head>
       <body>
          <div id = "computed_props">
             千米 : <input type = "text" v-model = "kilometers">
             米 : <input type = "text" v-model = "meters">
          </div>
    	   <p id="info"></p>
          <script type = "text/javascript">
             var vm = new Vue({
                el: '#computed_props',
                data: {
                   kilometers : 0,
                   meters:0
                },
                methods: {
                },
                computed :{
                },
                watch : {
                   kilometers:function(val) {
                      this.kilometers = val;
                      this.meters = this.kilometers * 1000
                   },
                   meters : function (val) {
                      this.kilometers = val/ 1000;
                      this.meters = val;
                   }
                }
             });
             // $watch 是一个实例方法
    		vm.$watch('kilometers', function (newValue, oldValue) {
    			// 这个回调将在 vm.kilometers 改变后调用
    		    document.getElementById ("info").innerHTML = "修改前值为: " + oldValue + ",修改后值为: " + newValue;
    		})
          </script>
       </body>
    </html>
    

    以上代码中我们创建了两个输入框,data 属性中, kilometers 和 meters 初始值都为 0。watch 对象创建了两个方法 kilometers 和 meters。当我们再输入框输入数据时,watch 会实时监听数据变化并改变自身的值。

    样式绑定

    class 与 style 是 HTML 元素的属性,用于设置元素的样式,我们可以用 v-bind 来设置样式属性。

    Vue.js v-bind 在处理 class 和 style 时, 专门增强了它。表达式的结果类型除了字符串之外,还可以是对象或数组。

    我们可以为 v-bind:class 设置一个对象,从而动态的切换 class:

    <div v-bind:class="{ 'active': isActive }"></div>
    

    实例中将 isActive 设置为 true 显示了一个绿色的 div 块,如果设置为 false 则不显示

    我们也可以在对象中传入更多属性用来动态切换多个 class :

    <div class="static"
         v-bind:class="{ 'active' : isActive, 'text-danger' : hasError }">
    </div>
    

    我们可以把一个数组传给 v-bind:class

    <div v-bind:class="[activeClass, errorClass]"></div>
    

    我们可以在 v-bind:style 直接设置内联样式:

    <div id="app">
        <div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }">菜鸟教程</div>
    </div>
    

    也可以直接绑定到一个样式对象,让模板更清晰:

    <div id="app">
      <div v-bind:style="styleObject">菜鸟教程</div>
    </div>
    
    <script>
    new Vue({
      el: '#app',
      data: {
        styleObject: {
          color: 'green',
          fontSize: '30px'
        }
      }
    })
    </script>
    

    v-bind:style 可以使用数组将多个样式对象应用到一个元素上:

    <div id="app">
      <div v-bind:style="[baseStyles, overridingStyles]">菜鸟教程</div>
    </div>
    

    事件处理器

    事件监听可以使用 v-on 指令(内联):

    <div id="app">
      <button v-on:click="counter += 1">增加 1</button>
      <p>这个按钮被点击了 {{ counter }} 次。</p>
    </div>
     
    <script>
    new Vue({
      el: '#app',
      data: {
        counter: 0
      }
    })
    </script>
    

    通常情况下,我们需要使用一个方法来调用 JavaScript 方法

    <div id="app">
       <!-- `greet` 是在下面定义的方法名 -->
      <button v-on:click="greet">Greet</button>
    </div>
     
    <script>
    var app = new Vue({
      el: '#app',
      data: {
        name: 'Vue.js'
      },
      // 在 `methods` 对象中定义方法
      methods: {
        greet: function (event) {
          // `this` 在方法里指当前 Vue 实例
          alert('Hello ' + this.name + '!')
          // `event` 是原生 DOM 事件
          if (event) {
              alert(event.target.tagName)
          }
        }
      }
    })
    // 也可以用 JavaScript 直接调用方法
    app.greet() // -> 'Hello Vue.js!'
    </script>
    

    事件修饰符

    <!-- 阻止单击事件冒泡 -->
    <a v-on:click.stop="doThis"></a>
    <!-- 提交事件不再重载页面 -->
    <form v-on:submit.prevent="onSubmit"></form>
    <!-- 修饰符可以串联  -->
    <a v-on:click.stop.prevent="doThat"></a>
    <!-- 只有修饰符 -->
    <form v-on:submit.prevent></form>
    <!-- 添加事件侦听器时使用事件捕获模式 -->
    <div v-on:click.capture="doThis">...</div>
    <!-- 只当事件在该元素本身(而不是子元素)触发时触发回调 -->
    <div v-on:click.self="doThat">...</div>
    
    <!-- click 事件只能点击一次,2.1.4版本新增 -->
    <a v-on:click.once="doThis"></a>
    

    表单

    用 v-model 指令在表单控件元素上创建双向数据绑定,v-model 会根据控件类型自动选取正确的方法来更新元素

    修饰符lazy在默认情况下, v-model 在 input 事件中同步输入框的值与数据,但你可以添加一个修饰符 lazy ,从而转变为在 change 事件中同步

    <!-- 在 "change" 而不是 "input" 事件中更新 -->
    <input v-model.lazy="msg" >
    

    .number 如果想自动将用户的输入值转为 Number 类型(如果原值的转换结果为 NaN 则返回原值),可以添加一个修饰符 number 给 v-model 来处理输入值:

    <input v-model.number="age" type="number">
    

    .trim如果要自动过滤用户输入的首尾空格,可以添加 trim 修饰符到 v-model 上过滤输入:

    <input v-model.trim="msg">
    

    组件

    • 组件(Component)是 Vue.js 最强大的功能之一。
    • 组件可以扩展 HTML 元素,封装可重用的代码。
    • 组件系统让我们可以用独立可复用的小组件来构建大型应用,几乎任意类型的应用的界面都可以抽象为一个组件树。

    所有实例都能用全局组件。

    <div id="app">
        <runoob></runoob>
    </div>
     
    <script>
    // 注册
    Vue.component('runoob', {
      template: '<h1>自定义组件!</h1>'
    })
    // 创建根实例
    new Vue({
      el: '#app'
    })
    </script>
    

    我们也可以在实例选项中注册局部组件,这样组件只能在这个实例中使用

    <div id="app">
        <runoob></runoob>
    </div>
     
    <script>
    var Child = {
      template: '<h1>自定义组件!</h1>'
    }
     
    // 创建根实例
    new Vue({
      el: '#app',
      components: {
        // <runoob> 将只在父模板可用
        'runoob': Child
      }
    })
    </script>
    

    prop 是子组件用来接受父组件传递过来的数据的一个自定义属性。

    父组件的数据需要通过 props 把数据传给子组件,子组件需要显式地用 props 选项声明 “prop”:

    <div id="app">
        <child message="hello!"></child>
    </div>
     
    <script>
    // 注册
    Vue.component('child', {
      // 声明 props
      props: ['message'],
      // 同样也可以在 vm 实例中像 "this.message" 这样使用
      template: '<span>{{ message }}</span>'
    })
    // 创建根实例
    new Vue({
      el: '#app'
    })
    </script>
    

    类似于用 v-bind 绑定 HTML 特性到一个表达式,也可以用 v-bind 动态绑定 props 的值到父组件的数据中。每当父组件的数据变化时,该变化也会传导给子组件

    <div id="app">
        <div>
          <input v-model="parentMsg">
          <br>
          <child v-bind:message="parentMsg"></child>
        </div>
    </div>
     
    <script>
    // 注册
    Vue.component('child', {
      // 声明 props
      props: ['message'],
      // 同样也可以在 vm 实例中像 "this.message" 这样使用
      template: '<span>{{ message }}</span>'
    })
    // 创建根实例
    new Vue({
      el: '#app',
      data: {
        parentMsg: '父组件内容'
      }
    })
    </script>
    

    结语

    如果你发现文章有什么问题,欢迎留言指正。
    如果你觉得这篇文章还可以,别忘记点个赞加个关注再走哦。
    如果你不嫌弃,还可以关注微信公众号———梦码城(持续更新中)。
    梦码在这里感激不尽!!

    展开全文
  • 本人学习vue时候的学习笔记,以及工作共遇到的我认为需要记录的一些知识点
  • Vue进阶(一):Vue 学习资料汇总

    万次阅读 多人点赞 2018-05-01 22:41:22
    Vue进阶(一):Vue学习资料汇总 Vue开发环境搭建(Windows) nodejs官网http://nodejs.cn/下载安装包; 安装好之后,在cmd命令行窗口查看相应node、npm版本信息 node -v // 显示node版本 npm -v // 显示npm包...
  • Vue学习官网和Vue的书籍 目录结构

    千次阅读 2019-06-18 13:42:00
    Vue基础知识学习网站【中文】 https://cn.vuejs.org/v2/guide/ Vue路由知识学习网站【中文】 https://router.vuejs.org/zh/guide/ Vuex更为复杂的Vue知识学习网站【中文】 https://vuex.vuejs.org/zh/ Vue...
  • vuejs基础语法: ...
  • vue学习(二)—vue.js2.0全局API学习

    千次阅读 2018-03-28 17:47:34
    Vue.directive自定义指令 &lt;!DOCTYPE html&gt; &lt;html&gt; &lt;head&gt; &lt;meta charset="UTF-8"&gt; &lt;title&gt;Vue.directive自定义指令&...
  • 最全的Vue学习资料

    千次阅读 2017-12-06 21:33:16
    element ★13489 - 饿了么出品的Vue2的web UI工具套件Vux ★8133 - 基于Vue和WeUI的组件库iview ★6634 - 基于 Vuejs 的开源 UI 组件库mint-ui ★6253 - Vue 2的移动UI元素muse-ui ★3705 - 三端样式一致的响应...
  • Vue学习笔记——Vue-router

    万次阅读 多人点赞 2019-02-24 17:32:21
    第1节:Vue-router入门 1、解读router/index.js文件 import Vue from 'vue' //引入Vue import Router from 'vue-router' //引入vue-router import Hello from '@/components/Hello' //引入根目录下的Hello.vue...
  • vue学习经验 项目目录

    千次阅读 2018-05-24 14:03:04
    一.Vue的介绍 1.为什么使用框架 2.框架和库的区别 3.vue是什么 4.组件之间的传值 10. import 和 export 11.源码目录 12.name 的使用 15.computed 计算属性的使用 16.vue插件大全...
  • Vue 学习

    千次阅读 2020-02-11 23:35:20
    Vue TodoList 项目 Vue初识之 TodoList Part1 Vue初识之 TodoList Part2 Vue初识之 TodoList Part3
  • vue学习步骤--开发笔记

    千次阅读 2018-08-30 10:45:01
    vue安装使用脚手架工具: ...# 全局安装 vue-cli 1.npm install -g vue-cli;全局安装脚手架工具快速构建vue项目的工具 2. # 创建一个基于 webpack 模板的新项目 vue init webpack myvuede...
  • Vue学习从入门到精通(一)

    万次阅读 多人点赞 2018-08-08 18:54:32
      最近公司由于业务拓展,需要进行小程序相关的开发,本着朝全栈开发者努力,决定学习Vue,去年csdn送了一本《Vue.js权威指南》,那就从这本书开始练起来吧。哟吼。 一,环境搭建 今天主要说一下如何...
  • 最近公司的老的项目重构,改成了前后端分离的架构,前端采用的是vue.js前端框架,所以接触到vue.js。 Vue.js是当下很火的一个JavaScript MVVM(Model-View-ViewModel)库,它是以数据驱动和组件化的思想构建的。相较于...
  • vue前端框架使用越来越多,而vscode作为前端开发IDE环境也使用越来越普遍,本文结合vscode介绍vue开发第一个vue项目。 开发vue需要依赖于nodejs,本博客有介绍nodejs的安装,本文就不再做介绍。 一、安装vue及依赖 ...
  • 在假期来临前,花了一个月快速学习掌握vue,从入门到现在也已经过了小半年时间,中间有用vue做个人博客网站、也学了mpvue开发微信小程序参加比赛并获奖,还学了python flask 和java。这段忙碌的时光带给我很多收获,...
  • Vue2.0学习教程PPT

    2018-11-27 17:06:56
    Vue.js基础入门PPT,非常适合初学者及有PPT制作需求的同学。
  • Vue.js学习文档

    2017-06-12 20:48:46
    Vue.js 架构学习
  • 首先,打开cmd,切换到你想放置项目的文件夹,如我想放到D:/学习,就如下设置 然后使用vue create 创建一个项目,名字任取,我取做vue_test,然后它会让你选择默认,还是手动添加,我们选择第二个,即 Manually se....
  • 【Vue】Vue学习资料

    千次阅读 2020-07-28 16:04:27
    因项目需要,最近学习了前端开发Vue,主要参考材料: 1、初识HTML+CSShttps://www.imooc.com/learn/9 2、vue.js教程https://www.runoob.com/vue2/vue-tutorial.html 3、基于vue+vant搭建H5通用架子...
  • vue初步学习ppt

    2016-12-09 09:38:09
    初步认识vue 简单ppt
  • vue学习二(搭建vue脚手架)

    千次阅读 2019-05-15 18:26:52
    1 安装node环境 ...使用 npm install --global vue-cli安装vue。 3.1创建第一个vue脚手架(命令行) 1.使用vue init webpack 项目名 创建项目 2.npm run dev 运行项目 3.2 使用webstorm 1.创建项目 ...
  • vue学习——v-for语句

    万次阅读 2018-10-16 00:05:11
    学习v-for语句之前,先了解一下vue是怎么数据绑定的 数据绑定最常见的形式就是使用“Mustache”语法 (双大括号) 的文本插值: &amp;lt;span&amp;gt;Message: {{ msg }}&amp;lt;/span&amp;gt; ...
  • Vue学习过程

    万次阅读 2020-07-18 10:22:00
    查看是否node安装成功:node -v 安装vue-Cli: npm install -g @vue/cli...vue create vue-demo 配置npm的淘宝镜像: npm config set registry https://registry.npm.taobao.org 查看npm的prefix和cache路径.

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 180,749
精华内容 72,299
关键字:

vue学习

vue 订阅