Vue基础

作者: JLong | 来源:发表于2019-05-06 19:40 被阅读0次
    MVC和MVVM的关系图解

    ### 什么是Vue.js

    + Vue.js 是目前最火的一个前端框架,React是最流行的一个前端框架(React除了开发网站,还可以开发手机App, Vue语法也是可以用于进行手机App开发的,需要借助于Weex)

    + Vue.js 是前端的**主流框架之一**,和Angular.js、React.js 一起,并成为前端三大主流框架!

    + Vue.js 是一套构建用户界面的框架,**只关注视图层**,它不仅易于上手,还便于与第三方库或既有项目整合。(Vue有配套的第三方类库,可以整合起来做大型项目的开发)

    + 前端的主要工作?主要负责MVC中的V这一层;主要工作就是和界面打交道,来制作前端页面效果;

    ## 为什么要学习流行框架

    + 企业为了提高开发效率:在企业中,时间就是效率,效率就是金钱;

      - 企业中,使用框架,能够提高开发的效率;

    + 提高开发效率的发展历程:原生JS -> Jquery之类的类库 -> 前端模板引擎 -> Angular.js / Vue.js(能够帮助我们减少不必要的DOM操作;提高渲染效率;双向数据绑定的概念【通过框架提供的指令,我们前端程序员只需要关心数据的业务逻辑,不再关心DOM是如何渲染的了】)

    + 在Vue中,一个核心的概念,就是让用户不再操作DOM元素,解放了用户的双手,让程序员可以更多的时间去关注业务逻辑;

    + 增强自己就业时候的竞争力

      - 人无我有,人有我优

      - 你平时不忙的时候,都在干嘛?

    ## 框架和库的区别

    + 框架:是一套完整的解决方案;对项目的侵入性较大,项目如果需要更换框架,则需要重新架构整个项目。

      - node 中的 express;

    + 库(插件):提供某一个小功能,对项目的侵入性较小,如果某个库无法完成某些需求,可以很容易切换到其它库实现需求。

      - 1. 从Jquery 切换到 Zepto

      - 2. 从 EJS 切换到 art-template

    ## Node(后端)中的 MVC 与 前端中的 MVVM 之间的区别

    + MVC 是后端的分层开发概念;

    + MVVM是前端视图层的概念,主要关注于 视图层分离,也就是说:MVVM把前端的视图层,分为了 三部分 Model, View , VM ViewModel

    + 为什么有了MVC还要有MVVM

    ## Vue.js 基本代码 和 MVVM 之间的对应关系

    ## Vue之 - `基本的代码结构`和`插值表达式`、`v-cloak`

    ## Vue指令之`v-text`和`v-html`

    ## Vue指令之`v-bind`的三种用法

    1. 直接使用指令`v-bind`

    2. 使用简化指令`:`

    3. 在绑定的时候,拼接绑定内容:`:title="btnTitle + ', 这是追加的内容'"`

    ## Vue指令之`v-on`和`跑马灯效果`

    ### 跑马灯效果

    1. HTML结构:

    ```

    <div id="app">

        <p>{{info}}</p>

        <input type="button" value="开启" v-on:click="go">

        <input type="button" value="停止" v-on:click="stop">

      </div>

    ```

    2. Vue实例:

    ```

    // 创建 Vue 实例,得到 ViewModel

        var vm = new Vue({

          el: '#app',

          data: {

            info: '猥琐发育,别浪~!',

            intervalId: null

          },

          methods: {

            go() {

              // 如果当前有定时器在运行,则直接return

              if (this.intervalId != null) {

                return;

              }

              // 开始定时器

              this.intervalId = setInterval(() => {

                this.info = this.info.substring(1) + this.info.substring(0, 1);

              }, 500);

            },

            stop() {

              clearInterval(this.intervalId);

            }

          }

        });

    ```

    ## Vue指令之`v-on的缩写`和`事件修饰符`

    ### 事件修饰符:

    + .stop      阻止冒泡

    + .prevent    阻止默认事件

    + .capture    添加事件侦听器时使用事件捕获模式

    + .self      只当事件在该元素本身(比如不是子元素)触发时触发回调

    + .once      事件只触发一次

    ## Vue指令之`v-model`和`双向数据绑定`

    ## 简易计算器案例

    1. HTML 代码结构

    ```

      <div id="app">

        <input type="text" v-model="n1">

        <select v-model="opt">

          <option value="0">+</option>

          <option value="1">-</option>

          <option value="2">*</option>

          <option value="3">÷</option>

        </select>

        <input type="text" v-model="n2">

        <input type="button" value="=" v-on:click="getResult">

        <input type="text" v-model="result">

      </div>

    ```

    2. Vue实例代码:

    ```

    // 创建 Vue 实例,得到 ViewModel

        var vm = new Vue({

          el: '#app',

          data: {

            n1: 0,

            n2: 0,

            result: 0,

            opt: '0'

          },

          methods: {

            getResult() {

              switch (this.opt) {

                case '0':

                  this.result = parseInt(this.n1) + parseInt(this.n2);

                  break;

                case '1':

                  this.result = parseInt(this.n1) - parseInt(this.n2);

                  break;

                case '2':

                  this.result = parseInt(this.n1) * parseInt(this.n2);

                  break;

                case '3':

                  this.result = parseInt(this.n1) / parseInt(this.n2);

                  break;

              }

            }

          }

        });

    ```

    ## 在Vue中使用样式

    ### 使用class样式

    1. 数组

    ```

    <h1 :class="['red', 'thin']">这是一个邪恶的H1</h1>

    ```

    2. 数组中使用三元表达式

    ```

    <h1 :class="['red', 'thin', isactive?'active':'']">这是一个邪恶的H1</h1>

    ```

    3. 数组中嵌套对象

    ```

    <h1 :class="['red', 'thin', {'active': isactive}]">这是一个邪恶的H1</h1>

    ```

    4. 直接使用对象

    ```

    <h1 :class="{red:true, italic:true, active:true, thin:true}">这是一个邪恶的H1</h1>

    ```

    ### 使用内联样式

    1. 直接在元素上通过 `:style` 的形式,书写样式对象

    ```

    <h1 :style="{color: 'red', 'font-size': '40px'}">这是一个善良的H1</h1>

    ```

    2. 将样式对象,定义到 `data` 中,并直接引用到 `:style` 中

    + 在data上定义样式:

    ```

    data: {

            h1StyleObj: { color: 'red', 'font-size': '40px', 'font-weight': '200' }

    }

    ```

    + 在元素中,通过属性绑定的形式,将样式对象应用到元素中:

    ```

    <h1 :style="h1StyleObj">这是一个善良的H1</h1>

    ```

    3. 在 `:style` 中通过数组,引用多个 `data` 上的样式对象

    + 在data上定义样式:

    ```

    data: {

            h1StyleObj: { color: 'red', 'font-size': '40px', 'font-weight': '200' },

            h1StyleObj2: { fontStyle: 'italic' }

    }

    ```

    + 在元素中,通过属性绑定的形式,将样式对象应用到元素中:

    ```

    <h1 :style="[h1StyleObj, h1StyleObj2]">这是一个善良的H1</h1>

    ```

    ## Vue指令之`v-for`和`key`属性

    1. 迭代数组

    ```

    <ul>

      <li v-for="(item, i) in list">索引:{{i}} --- 姓名:{{item.name}} --- 年龄:{{item.age}}</li>

    </ul>

    ```

    2. 迭代对象中的属性

    ```

    <!-- 循环遍历对象身上的属性 -->

        <div v-for="(val, key, i) in userInfo">{{val}} --- {{key}} --- {{i}}</div>

    ```

    3. 迭代数字

    ```

    <p v-for="i in 10">这是第 {{i}} 个P标签</p>

    ```

    > 2.2.0+ 的版本里,**当在组件中使用** v-for 时,key 现在是必须的。

    当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用 “**就地复用**” 策略。如果数据项的顺序被改变,Vue将**不是移动 DOM 元素来匹配数据项的顺序**, 而是**简单复用此处每个元素**,并且确保它在特定索引下显示已被渲染过的每个元素。

    为了给 Vue 一个提示,**以便它能跟踪每个节点的身份,从而重用和重新排序现有元素**,你需要为每项提供一个唯一 key 属性。

    ## Vue指令之`v-if`和`v-show`

    > 一般来说,v-if 有更高的切换消耗而 v-show 有更高的初始渲染消耗。因此,如果需要频繁切换 v-show 较好,如果在运行时条件不大可能改变 v-if 较好。


    ## 品牌管理案例

    ### 添加新品牌

    ### 删除品牌

    ### 根据条件筛选品牌

    1. 1.x 版本中的filterBy指令,在2.x中已经被废除:

    [filterBy - 指令](https://v1-cn.vuejs.org/api/#filterBy)

    ```

    <tr v-for="item in list | filterBy searchName in 'name'">

      <td>{{item.id}}</td>

      <td>{{item.name}}</td>

      <td>{{item.ctime}}</td>

      <td>

        <a href="#" @click.prevent="del(item.id)">删除</a>

      </td>

    </tr>

    ```

    2. 在2.x版本中[手动实现筛选的方式](https://cn.vuejs.org/v2/guide/list.html#显示过滤-排序结果):

    + 筛选框绑定到 VM 实例中的 `searchName` 属性:

    ```

    <hr> 输入筛选名称:

    <input type="text" v-model="searchName">

    ```

    + 在使用 `v-for` 指令循环每一行数据的时候,不再直接 `item in list`,而是 `in` 一个 过滤的methods 方法,同时,把过滤条件`searchName`传递进去:

    ```

    <tbody>

          <tr v-for="item in search(searchName)">

            <td>{{item.id}}</td>

            <td>{{item.name}}</td>

            <td>{{item.ctime}}</td>

            <td>

              <a href="#" @click.prevent="del(item.id)">删除</a>

            </td>

          </tr>

        </tbody>

    ```

    + `search` 过滤方法中,使用 数组的 `filter` 方法进行过滤:

    ```

    search(name) {

      return this.list.filter(x => {

        return x.name.indexOf(name) != -1;

      });

    }

    ```

    ## Vue调试工具`vue-devtools`的安装步骤和使用

    [Vue.js devtools - 翻墙安装方式 - 推荐](https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd?hl=zh-CN)

    ## 过滤器

    概念:Vue.js 允许你自定义过滤器,**可被用作一些常见的文本格式化**。过滤器可以用在两个地方:**mustache 插值和 v-bind 表达式**。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符指示;

    ### 私有过滤器

    1. HTML元素:

    ```

    <td>{{item.ctime | dataFormat('yyyy-mm-dd')}}</td>

    ```

    2. 私有 `filters` 定义方式:

    ```

    filters: { // 私有局部过滤器,只能在 当前 VM 对象所控制的 View 区域进行使用

        dataFormat(input, pattern = "") { // 在参数列表中 通过 pattern="" 来指定形参默认值,防止报错

          var dt = new Date(input);

          // 获取年月日

          var y = dt.getFullYear();

          var m = (dt.getMonth() + 1).toString().padStart(2, '0');

          var d = dt.getDate().toString().padStart(2, '0');

          // 如果 传递进来的字符串类型,转为小写之后,等于 yyyy-mm-dd,那么就返回 年-月-日

          // 否则,就返回  年-月-日 时:分:秒

          if (pattern.toLowerCase() === 'yyyy-mm-dd') {

            return `${y}-${m}-${d}`;

          } else {

            // 获取时分秒

            var hh = dt.getHours().toString().padStart(2, '0');

            var mm = dt.getMinutes().toString().padStart(2, '0');

            var ss = dt.getSeconds().toString().padStart(2, '0');

            return `${y}-${m}-${d} ${hh}:${mm}:${ss}`;

          }

        }

      }

    ```

    > 使用ES6中的字符串新方法 String.prototype.padStart(maxLength, fillString='') 或 String.prototype.padEnd(maxLength, fillString='')来填充字符串;

    ### 全局过滤器

    ```

    // 定义一个全局过滤器

    Vue.filter('dataFormat', function (input, pattern = '') {

      var dt = new Date(input);

      // 获取年月日

      var y = dt.getFullYear();

      var m = (dt.getMonth() + 1).toString().padStart(2, '0');

      var d = dt.getDate().toString().padStart(2, '0');

      // 如果 传递进来的字符串类型,转为小写之后,等于 yyyy-mm-dd,那么就返回 年-月-日

      // 否则,就返回  年-月-日 时:分:秒

      if (pattern.toLowerCase() === 'yyyy-mm-dd') {

        return `${y}-${m}-${d}`;

      } else {

        // 获取时分秒

        var hh = dt.getHours().toString().padStart(2, '0');

        var mm = dt.getMinutes().toString().padStart(2, '0');

        var ss = dt.getSeconds().toString().padStart(2, '0');

        return `${y}-${m}-${d} ${hh}:${mm}:${ss}`;

      }

    });

    ```

    > 注意:当有局部和全局两个名称相同的过滤器时候,会以就近原则进行调用,即:局部过滤器优先于全局过滤器被调用!

    ## 键盘修饰符以及自定义键盘修饰符

    ### 1.x中自定义键盘修饰符【了解即可】

    ```

    Vue.directive('on').keyCodes.f2 = 113;

    ```

    ### [2.x中自定义键盘修饰符](https://cn.vuejs.org/v2/guide/events.html#键值修饰符)

    1. 通过`Vue.config.keyCodes.名称 = 按键值`来自定义案件修饰符的别名:

    ```

    Vue.config.keyCodes.f2 = 113;

    ```

    2. 使用自定义的按键修饰符:

    ```

    <input type="text" v-model="name" @keyup.f2="add">

    ```

    ## [自定义指令](https://cn.vuejs.org/v2/guide/custom-directive.html)

    1. 自定义全局和局部的 自定义指令:

    ```

        // 自定义全局指令 v-focus,为绑定的元素自动获取焦点:

        Vue.directive('focus', {

          inserted: function (el) { // inserted 表示被绑定元素插入父节点时调用

            el.focus();

          }

        });

        // 自定义局部指令 v-color 和 v-font-weight,为绑定的元素设置指定的字体颜色 和 字体粗细:

          directives: {

            color: { // 为元素设置指定的字体颜色

              bind(el, binding) {

                el.style.color = binding.value;

              }

            },

            'font-weight': function (el, binding2) { // 自定义指令的简写形式,等同于定义了 bind 和 update 两个钩子函数

              el.style.fontWeight = binding2.value;

            }

          }

    ```

    2. 自定义指令的使用方式:

    ```

    <input type="text" v-model="searchName" v-focus v-color="'red'" v-font-weight="900">

    ```

    ## Vue 1.x 中 自定义元素指令【已废弃,了解即可】

    ```

    Vue.elementDirective('red-color', {

      bind: function () {

        this.el.style.color = 'red';

      }

    });

    ```

    使用方式:

    ```

    <red-color>1232</red-color>

    ```

    ## [vue实例的生命周期](https://cn.vuejs.org/v2/guide/instance.html#实例生命周期)

    lifecycle

    + 什么是生命周期:从Vue实例创建、运行、到销毁期间,总是伴随着各种各样的事件,这些事件,统称为生命周期!

    + [生命周期钩子](https://cn.vuejs.org/v2/api/#选项-生命周期钩子):就是生命周期事件的别名而已;

    + 生命周期钩子 = 生命周期函数 = 生命周期事件

    + 主要的生命周期函数分类:

    - 创建期间的生命周期函数:

      + beforeCreate:实例刚在内存中被创建出来,此时,还没有初始化好 data 和 methods 属性

      + created:实例已经在内存中创建OK,此时 data 和 methods 已经创建OK,此时还没有开始 编译模板

      + beforeMount:此时已经完成了模板的编译,但是还没有挂载到页面中

      + mounted:此时,已经将编译好的模板,挂载到了页面指定的容器中显示

    - 运行期间的生命周期函数:

    + beforeUpdate:状态更新之前执行此函数, 此时 data 中的状态值是最新的,但是界面上显示的 数据还是旧的,因为此时还没有开始重新渲染DOM节点

    + updated:实例更新完毕之后调用此函数,此时 data 中的状态值 和 界面上显示的数据,都已经完成了更新,界面已经被重新渲染好了!

    - 销毁期间的生命周期函数:

    + beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用。

    + destroyed:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。

    ## [vue-resource 实现 get, post, jsonp请求](https://github.com/pagekit/vue-resource)

    除了 vue-resource 之外,还可以使用 `axios` 的第三方包实现实现数据的请求

    1. 之前的学习中,如何发起数据请求?

    2. 常见的数据请求类型?  get  post jsonp

    3. 测试的URL请求资源地址:

    + get请求地址: http://vue.studyit.io/api/getlunbo

    + post请求地址:http://vue.studyit.io/api/post

    + jsonp请求地址:http://vue.studyit.io/api/jsonp

    4. JSONP的实现原理

    + 由于浏览器的安全性限制,不允许AJAX访问 协议不同、域名不同、端口号不同的 数据接口,浏览器认为这种访问不安全;

    + 可以通过动态创建script标签的形式,把script标签的src属性,指向数据接口的地址,因为script标签不存在跨域限制,这种数据获取方式,称作JSONP(注意:根据JSONP的实现原理,知晓,JSONP只支持Get请求);

    + 具体实现过程:

    - 先在客户端定义一个回调方法,预定义对数据的操作;

    - 再把这个回调方法的名称,通过URL传参的形式,提交到服务器的数据接口;

    - 服务器数据接口组织好要发送给客户端的数据,再拿着客户端传递过来的回调方法名称,拼接出一个调用这个方法的字符串,发送给客户端去解析执行;

    - 客户端拿到服务器返回的字符串之后,当作Script脚本去解析执行,这样就能够拿到JSONP的数据了;

    + 带大家通过 Node.js ,来手动实现一个JSONP的请求例子;

    ```

        const http = require('http');

        // 导入解析 URL 地址的核心模块

        const urlModule = require('url');

        const server = http.createServer();

        // 监听 服务器的 request 请求事件,处理每个请求

        server.on('request', (req, res) => {

          const url = req.url;

          // 解析客户端请求的URL地址

          var info = urlModule.parse(url, true);

          // 如果请求的 URL 地址是 /getjsonp ,则表示要获取JSONP类型的数据

          if (info.pathname === '/getjsonp') {

            // 获取客户端指定的回调函数的名称

            var cbName = info.query.callback;

            // 手动拼接要返回给客户端的数据对象

            var data = {

              name: 'zs',

              age: 22,

              gender: '男',

              hobby: ['吃饭', '睡觉', '运动']

            }

            // 拼接出一个方法的调用,在调用这个方法的时候,把要发送给客户端的数据,序列化为字符串,作为参数传递给这个调用的方法:

            var result = `${cbName}(${JSON.stringify(data)})`;

            // 将拼接好的方法的调用,返回给客户端去解析执行

            res.end(result);

          } else {

            res.end('404');

          }

        });

        server.listen(3000, () => {

          console.log('server running at http://127.0.0.1:3000');

        });

    ```

    5. vue-resource 的配置步骤:

    + 直接在页面中,通过`script`标签,引入 `vue-resource` 的脚本文件;

    + 注意:引用的先后顺序是:先引用 `Vue` 的脚本文件,再引用 `vue-resource` 的脚本文件;

    6. 发送get请求:

    ```

    getInfo() { // get 方式获取数据

      this.$http.get('http://127.0.0.1:8899/api/getlunbo').then(res => {

        console.log(res.body);

      })

    }

    ```

    7. 发送post请求:

    ```

    postInfo() {

      var url = 'http://127.0.0.1:8899/api/post';

      // post 方法接收三个参数:

      // 参数1: 要请求的URL地址

      // 参数2: 要发送的数据对象

      // 参数3: 指定post提交的编码类型为 application/x-www-form-urlencoded

      this.$http.post(url, { name: 'zs' }, { emulateJSON: true }).then(res => {

        console.log(res.body);

      });

    }

    ```

    8. 发送JSONP请求获取数据:

    ```

    jsonpInfo() { // JSONP形式从服务器获取数据

      var url = 'http://127.0.0.1:8899/api/jsonp';

      this.$http.jsonp(url).then(res => {

        console.log(res.body);

      });

    }

    ```

    ## 配置本地数据库和数据接口API

    1. 先解压安装 `PHPStudy`;

    2. 解压安装 `Navicat` 这个数据库可视化工具,并激活;

    3. 打开 `Navicat` 工具,新建空白数据库,名为 `dtcmsdb4`;

    4. 双击新建的数据库,连接上这个空白数据库,在新建的数据库上`右键` -> `运行SQL文件`,选择并执行 `dtcmsdb4.sql` 这个数据库脚本文件;如果执行不报错,则数据库导入完成;

    5. 进入文件夹 `vuecms3_nodejsapi` 内部,执行 `npm i` 安装所有的依赖项;

    6. 先确保本机安装了 `nodemon`, 没有安装,则运行 `npm i nodemon -g` 进行全局安装,安装完毕后,进入到 `vuecms3_nodejsapi`目录 -> `src`目录 -> 双击运行 `start.bat`

    7. 如果API启动失败,请检查 PHPStudy 是否正常开启,同时,检查 `app.js` 中第 `14行` 中数据库连接配置字符串是否正确;PHPStudy 中默认的 用户名是root,默认的密码也是root

    ## [Vue中的动画](https://cn.vuejs.org/v2/guide/transitions.html)

    为什么要有动画:动画能够提高用户的体验,帮助用户更好的理解页面中的功能;

    ### 使用过渡类名

    1. HTML结构:

    ```

    <div id="app">

        <input type="button" value="动起来" @click="myAnimate">

        <!-- 使用 transition 将需要过渡的元素包裹起来 -->

        <transition name="fade">

          <div v-show="isshow">动画哦</div>

        </transition>

      </div>

    ```

    2. VM 实例:

    ```

    // 创建 Vue 实例,得到 ViewModel

    var vm = new Vue({

      el: '#app',

      data: {

        isshow: false

      },

      methods: {

        myAnimate() {

          this.isshow = !this.isshow;

        }

      }

    });

    ```

    3. 定义两组类样式:

    ```

    /* 定义进入和离开时候的过渡状态 */

        .fade-enter-active,

        .fade-leave-active {

          transition: all 0.2s ease;

          position: absolute;

        }

        /* 定义进入过渡的开始状态 和 离开过渡的结束状态 */

        .fade-enter,

        .fade-leave-to {

          opacity: 0;

          transform: translateX(100px);

        }

    ```

    ### [使用第三方 CSS 动画库](https://cn.vuejs.org/v2/guide/transitions.html#自定义过渡类名)

    1. 导入动画类库:

    ```

    <link rel="stylesheet" type="text/css" href="./lib/animate.css">

    ```

    2. 定义 transition 及属性:

    ```

    <transition

    enter-active-class="fadeInRight"

        leave-active-class="fadeOutRight"

        :duration="{ enter: 500, leave: 800 }">

      <div class="animated" v-show="isshow">动画哦</div>

    </transition>

    ```

    ### 使用动画钩子函数

    1. 定义 transition 组件以及三个钩子函数:

    ```

    <div id="app">

        <input type="button" value="切换动画" @click="isshow = !isshow">

        <transition

        @before-enter="beforeEnter"

        @enter="enter"

        @after-enter="afterEnter">

          <div v-if="isshow" class="show">OK</div>

        </transition>

      </div>

    ```

    2. 定义三个 methods 钩子方法:

    ```

    methods: {

            beforeEnter(el) { // 动画进入之前的回调

              el.style.transform = 'translateX(500px)';

            },

            enter(el, done) { // 动画进入完成时候的回调

              el.offsetWidth;

              el.style.transform = 'translateX(0px)';

              done();

            },

            afterEnter(el) { // 动画进入完成之后的回调

              this.isshow = !this.isshow;

            }

          }

    ```

    3. 定义动画过渡时长和样式:

    ```

    .show{

          transition: all 0.4s ease;

        }

    ```

    ### [v-for 的列表过渡](https://cn.vuejs.org/v2/guide/transitions.html#列表的进入和离开过渡)

    1. 定义过渡样式:

    ```

    <style>

        .list-enter,

        .list-leave-to {

          opacity: 0;

          transform: translateY(10px);

        }

        .list-enter-active,

        .list-leave-active {

          transition: all 0.3s ease;

        }

    </style>

    ```

    2. 定义DOM结构,其中,需要使用 transition-group 组件把v-for循环的列表包裹起来:

    ```

      <div id="app">

        <input type="text" v-model="txt" @keyup.enter="add">

        <transition-group tag="ul" name="list">

          <li v-for="(item, i) in list" :key="i">{{item}}</li>

        </transition-group>

      </div>

    ```

    3. 定义 VM中的结构:

    ```

        // 创建 Vue 实例,得到 ViewModel

        var vm = new Vue({

          el: '#app',

          data: {

            txt: '',

            list: [1, 2, 3, 4]

          },

          methods: {

            add() {

              this.list.push(this.txt);

              this.txt = '';

            }

          }

        });

    ```

    ### 列表的排序过渡

    `<transition-group>` 组件还有一个特殊之处。不仅可以进入和离开动画,**还可以改变定位**。要使用这个新功能只需了解新增的 `v-move` 特性,**它会在元素的改变定位的过程中应用**。

    + `v-move` 和 `v-leave-active` 结合使用,能够让列表的过渡更加平缓柔和:

    ```

    .v-move{

      transition: all 0.8s ease;

    }

    .v-leave-active{

      position: absolute;

    }

    ```

    ## 相关文章

    1. [vue.js 1.x 文档](https://v1-cn.vuejs.org/)

    2. [vue.js 2.x 文档](https://cn.vuejs.org/)

    3. [String.prototype.padStart(maxLength, fillString)](http://www.css88.com/archives/7715)

    4. [js 里面的键盘事件对应的键码](http://www.cnblogs.com/wuhua1/p/6686237.html)

    5. [pagekit/vue-resource](https://github.com/pagekit/vue-resource)

    6. [navicat如何导入sql文件和导出sql文件](https://jingyan.baidu.com/article/a65957f4976aad24e67f9b9b.html)

    7. [贝塞尔在线生成器](http://cubic-bezier.com/#.4,-0.3,1,.33)

    ## 定义Vue组件

    什么是组件: 组件的出现,就是为了拆分Vue实例的代码量的,能够让我们以不同的组件,来划分不同的功能模块,将来我们需要什么样的功能,就可以去调用对应的组件即可;

    组件化和模块化的不同:

    + 模块化: 是从代码逻辑的角度进行划分的;方便代码分层开发,保证每个功能模块的职能单一;

    + 组件化: 是从UI界面的角度进行划分的;前端的组件化,方便UI组件的重用;

    ### 全局组件定义的三种方式

    1. 使用 Vue.extend 配合 Vue.component 方法:

    ```

    var login = Vue.extend({

          template: '<h1>登录</h1>'

        });

        Vue.component('login', login);

    ```

    2. 直接使用 Vue.component 方法:

    ```

    Vue.component('register', {

          template: '<h1>注册</h1>'

        });

    ```

    3. 将模板字符串,定义到script标签种:

    ```

    <script id="tmpl" type="x-template">

          <div><a href="#">登录</a> | <a href="#">注册</a></div>

        </script>

    ```

    同时,需要使用 Vue.component 来定义组件:

    ```

    Vue.component('account', {

          template: '#tmpl'

        });

    ```

    > 注意: 组件中的DOM结构,有且只能有唯一的根元素(Root Element)来进行包裹!

    ### 组件中展示数据和响应事件

    1. 在组件中,`data`需要被定义为一个方法,例如:

    ```

    Vue.component('account', {

          template: '#tmpl',

          data() {

            return {

              msg: '大家好!'

            }

          },

          methods:{

            login(){

              alert('点击了登录按钮');

            }

          }

        });

    ```

    2. 在子组件中,如果将模板字符串,定义到了script标签中,那么,要访问子组件身上的`data`属性中的值,需要使用`this`来访问;

    ### 【重点】为什么组件中的data属性必须定义为一个方法并返回一个对象

    1. 通过计数器案例演示

    ### 使用`components`属性定义局部子组件

    1. 组件实例定义方式:

    ```

    <script>

        // 创建 Vue 实例,得到 ViewModel

        var vm = new Vue({

          el: '#app',

          data: {},

          methods: {},

          components: { // 定义子组件

            account: { // account 组件

              template: '<div><h1>这是Account组件{{name}}</h1><login></login></div>', // 在这里使用定义的子组件

              components: { // 定义子组件的子组件

                login: { // login 组件

                  template: "<h3>这是登录组件</h3>"

                }

              }

            }

          }

        });

      </script>

    ```

    2. 引用组件:

    ```

    <div id="app">

        <account></account>

      </div>

    ```

    ## 使用`flag`标识符结合`v-if`和`v-else`切换组件

    1. 页面结构:

    ```

    <div id="app">

        <input type="button" value="toggle" @click="flag=!flag">

        <my-com1 v-if="flag"></my-com1>

        <my-com2 v-else="flag"></my-com2>

      </div>

    ```

    2. Vue实例定义:

    ```

    <script>

        Vue.component('myCom1', {

          template: '<h3>奔波霸</h3>'

        })

        Vue.component('myCom2', {

          template: '<h3>霸波奔</h3>'

        })

        // 创建 Vue 实例,得到 ViewModel

        var vm = new Vue({

          el: '#app',

          data: {

            flag: true

          },

          methods: {}

        });

      </script>

    ```

    ## 使用`:is`属性来切换不同的子组件,并添加切换动画

    1. 组件实例定义方式:

    ```

      // 登录组件

        const login = Vue.extend({

          template: `<div>

            <h3>登录组件</h3>

          </div>`

        });

        Vue.component('login', login);

        // 注册组件

        const register = Vue.extend({

          template: `<div>

            <h3>注册组件</h3>

          </div>`

        });

        Vue.component('register', register);

        // 创建 Vue 实例,得到 ViewModel

        var vm = new Vue({

          el: '#app',

          data: { comName: 'login' },

          methods: {}

        });

    ```

    2. 使用`component`标签,来引用组件,并通过`:is`属性来指定要加载的组件:

    ```

      <div id="app">

        <a href="#" @click.prevent="comName='login'">登录</a>

        <a href="#" @click.prevent="comName='register'">注册</a>

        <hr>

        <transition mode="out-in">

          <component :is="comName"></component>

        </transition>

      </div>

    ```

    3. 添加切换样式:

    ```

      <style>

        .v-enter,

        .v-leave-to {

          opacity: 0;

          transform: translateX(30px);

        }

        .v-enter-active,

        .v-leave-active {

          position: absolute;

          transition: all 0.3s ease;

        }

        h3{

          margin: 0;

        }

      </style>

    ```

    ## 父组件向子组件传值

    1. 组件实例定义方式,注意:一定要使用`props`属性来定义父组件传递过来的数据

    ```

    <script>

        // 创建 Vue 实例,得到 ViewModel

        var vm = new Vue({

          el: '#app',

          data: {

            msg: '这是父组件中的消息'

          },

          components: {

            son: {

              template: '<h1>这是子组件 --- {{finfo}}</h1>',

              props: ['finfo']

            }

          }

        });

      </script>

    ```

    2. 使用`v-bind`或简化指令,将数据传递到子组件中:

    ```

    <div id="app">

        <son :finfo="msg"></son>

      </div>

    ```

    ## 子组件向父组件传值

    1. 原理:父组件将方法的引用,传递到子组件内部,子组件在内部调用父组件传递过来的方法,同时把要发送给父组件的数据,在调用方法的时候当作参数传递进去;

    2. 父组件将方法的引用传递给子组件,其中,`getMsg`是父组件中`methods`中定义的方法名称,`func`是子组件调用传递过来方法时候的方法名称

    ```

    <son @func="getMsg"></son>

    ```

    3. 子组件内部通过`this.$emit('方法名', 要传递的数据)`方式,来调用父组件中的方法,同时把数据传递给父组件使用

    ```

    <div id="app">

        <!-- 引用父组件 -->

        <son @func="getMsg"></son>

        <!-- 组件模板定义 -->

        <script type="x-template" id="son">

          <div>

            <input type="button" value="向父组件传值" @click="sendMsg" />

          </div>

        </script>

      </div>

      <script>

        // 子组件的定义方式

        Vue.component('son', {

          template: '#son', // 组件模板Id

          methods: {

            sendMsg() { // 按钮的点击事件

              this.$emit('func', 'OK'); // 调用父组件传递过来的方法,同时把数据传递出去

            }

          }

        });

        // 创建 Vue 实例,得到 ViewModel

        var vm = new Vue({

          el: '#app',

          data: {},

          methods: {

            getMsg(val){ // 子组件中,通过 this.$emit() 实际调用的方法,在此进行定义

              alert(val);

            }

          }

        });

      </script>

    ```

    ## 评论列表案例

    目标:主要练习父子组件之间传值

    ## 使用 `this.$refs` 来获取元素和组件

    ```

      <div id="app">

        <div>

          <input type="button" value="获取元素内容" @click="getElement" />

          <!-- 使用 ref 获取元素 -->

          <h1 ref="myh1">这是一个大大的H1</h1>

          <hr>

          <!-- 使用 ref 获取子组件 -->

          <my-com ref="mycom"></my-com>

        </div>

      </div>

      <script>

        Vue.component('my-com', {

          template: '<h5>这是一个子组件</h5>',

          data() {

            return {

              name: '子组件'

            }

          }

        });

        // 创建 Vue 实例,得到 ViewModel

        var vm = new Vue({

          el: '#app',

          data: {},

          methods: {

            getElement() {

              // 通过 this.$refs 来获取元素

              console.log(this.$refs.myh1.innerText);

              // 通过 this.$refs 来获取组件

              console.log(this.$refs.mycom.name);

            }

          }

        });

      </script>

    ```

    ## 什么是路由

    1. 对于普通的网站,所有的超链接都是URL地址,所有的URL地址都对应服务器上对应的资源;

    2. 对于单页面应用程序来说,主要通过URL中的hash(#号)来实现不同页面之间的切换,同时,hash有一个特点:HTTP请求中不会包含hash相关的内容;所以,单页面程序中的页面跳转主要用hash实现;

    3. 在单页面应用程序中,这种通过hash改变来切换页面的方式,称作前端路由(区别于后端路由);

    ## 在 vue 中使用 vue-router

    1. 导入 vue-router 组件类库:

    ```

    <!-- 1. 导入 vue-router 组件类库 -->

      <script src="./lib/vue-router-2.7.0.js"></script>

    ```

    2. 使用 router-link 组件来导航

    ```

    <!-- 2. 使用 router-link 组件来导航 -->

    <router-link to="/login">登录</router-link>

    <router-link to="/register">注册</router-link>

    ```

    3. 使用 router-view 组件来显示匹配到的组件

    ```

    <!-- 3. 使用 router-view 组件来显示匹配到的组件 -->

    <router-view></router-view>

    ```

    4. 创建使用`Vue.extend`创建组件

    ```

        // 4.1 使用 Vue.extend 来创建登录组件

        var login = Vue.extend({

          template: '<h1>登录组件</h1>'

        });

        // 4.2 使用 Vue.extend 来创建注册组件

        var register = Vue.extend({

          template: '<h1>注册组件</h1>'

        });

    ```

    5. 创建一个路由 router 实例,通过 routers 属性来定义路由匹配规则

    ```

    // 5. 创建一个路由 router 实例,通过 routers 属性来定义路由匹配规则

        var router = new VueRouter({

          routes: [

            { path: '/login', component: login },

            { path: '/register', component: register }

          ]

        });

    ```

    6. 使用 router 属性来使用路由规则

    ```

    // 6. 创建 Vue 实例,得到 ViewModel

        var vm = new Vue({

          el: '#app',

          router: router // 使用 router 属性来使用路由规则

        });

    ```

    ## 设置路由高亮

    ## 设置路由切换动效

    ## 在路由规则中定义参数

    1. 在规则中定义参数:

    ```

    { path: '/register/:id', component: register }

    ```

    2. 通过 `this.$route.params`来获取路由中的参数:

    ```

    var register = Vue.extend({

          template: '<h1>注册组件 --- {{this.$route.params.id}}</h1>'

        });

    ```

    ## 使用 `children` 属性实现路由嵌套

    ```

      <div id="app">

        <router-link to="/account">Account</router-link>

        <router-view></router-view>

      </div>

      <script>

        // 父路由中的组件

        const account = Vue.extend({

          template: `<div>

            这是account组件

            <router-link to="/account/login">login</router-link> |

            <router-link to="/account/register">register</router-link>

            <router-view></router-view>

          </div>`

        });

        // 子路由中的 login 组件

        const login = Vue.extend({

          template: '<div>登录组件</div>'

        });

        // 子路由中的 register 组件

        const register = Vue.extend({

          template: '<div>注册组件</div>'

        });

        // 路由实例

        var router = new VueRouter({

          routes: [

            { path: '/', redirect: '/account/login' }, // 使用 redirect 实现路由重定向

            {

              path: '/account',

              component: account,

              children: [ // 通过 children 数组属性,来实现路由的嵌套

                { path: 'login', component: login }, // 注意,子路由的开头位置,不要加 / 路径符

                { path: 'register', component: register }

              ]

            }

          ]

        });

        // 创建 Vue 实例,得到 ViewModel

        var vm = new Vue({

          el: '#app',

          data: {},

          methods: {},

          components: {

            account

          },

          router: router

        });

      </script>

    ```

    ## 命名视图实现经典布局

    1. 标签代码结构:

    ```

    <div id="app">

        <router-view></router-view>

        <div class="content">

          <router-view name="a"></router-view>

          <router-view name="b"></router-view>

        </div>

      </div>

    ```

    2. JS代码:

    ```

    <script>

        var header = Vue.component('header', {

          template: '<div class="header">header</div>'

        });

        var sidebar = Vue.component('sidebar', {

          template: '<div class="sidebar">sidebar</div>'

        });

        var mainbox = Vue.component('mainbox', {

          template: '<div class="mainbox">mainbox</div>'

        });

        // 创建路由对象

        var router = new VueRouter({

          routes: [

            {

              path: '/', components: {

                default: header,

                a: sidebar,

                b: mainbox

              }

            }

          ]

        });

        // 创建 Vue 实例,得到 ViewModel

        var vm = new Vue({

          el: '#app',

          data: {},

          methods: {},

          router

        });

      </script>

    ```

    3. CSS 样式:

    ```

      <style>

        .header {

          border: 1px solid red;

        }

        .content{

          display: flex;

        }

        .sidebar {

          flex: 2;

          border: 1px solid green;

          height: 500px;

        }

        .mainbox{

          flex: 8;

          border: 1px solid blue;

          height: 500px;

        }

      </style>

    ```

    ## `watch`属性的使用

    考虑一个问题:想要实现 `名` 和 `姓` 两个文本框的内容改变,则全名的文本框中的值也跟着改变;(用以前的知识如何实现???)

    1. 监听`data`中属性的改变:

    ```

    <div id="app">

        <input type="text" v-model="firstName"> +

        <input type="text" v-model="lastName"> =

        <span>{{fullName}}</span>

      </div>

      <script>

        // 创建 Vue 实例,得到 ViewModel

        var vm = new Vue({

          el: '#app',

          data: {

            firstName: 'jack',

            lastName: 'chen',

            fullName: 'jack - chen'

          },

          methods: {},

          watch: {

            'firstName': function (newVal, oldVal) { // 第一个参数是新数据,第二个参数是旧数据

              this.fullName = newVal + ' - ' + this.lastName;

            },

            'lastName': function (newVal, oldVal) {

              this.fullName = this.firstName + ' - ' + newVal;

            }

          }

        });

      </script>

    ```

    2. 监听路由对象的改变:

    ```

    <div id="app">

        <router-link to="/login">登录</router-link>

        <router-link to="/register">注册</router-link>

        <router-view></router-view>

      </div>

      <script>

        var login = Vue.extend({

          template: '<h1>登录组件</h1>'

        });

        var register = Vue.extend({

          template: '<h1>注册组件</h1>'

        });

        var router = new VueRouter({

          routes: [

            { path: "/login", component: login },

            { path: "/register", component: register }

          ]

        });

        // 创建 Vue 实例,得到 ViewModel

        var vm = new Vue({

          el: '#app',

          data: {},

          methods: {},

          router: router,

          watch: {

            '$route': function (newVal, oldVal) {

              if (newVal.path === '/login') {

                console.log('这是登录组件');

              }

            }

          }

        });

      </script>

    ```

    ## `computed`计算属性的使用

    1. 默认只有`getter`的计算属性:

    ```

    <div id="app">

        <input type="text" v-model="firstName"> +

        <input type="text" v-model="lastName"> =

        <span>{{fullName}}</span>

      </div>

      <script>

        // 创建 Vue 实例,得到 ViewModel

        var vm = new Vue({

          el: '#app',

          data: {

            firstName: 'jack',

            lastName: 'chen'

          },

          methods: {},

          computed: { // 计算属性; 特点:当计算属性中所以来的任何一个 data 属性改变之后,都会重新触发 本计算属性 的重新计算,从而更新 fullName 的值

            fullName() {

              return this.firstName + ' - ' + this.lastName;

            }

          }

        });

      </script>

    ```

    2. 定义有`getter`和`setter`的计算属性:

    ```

    <div id="app">

        <input type="text" v-model="firstName">

        <input type="text" v-model="lastName">

        <!-- 点击按钮重新为 计算属性 fullName 赋值 -->

        <input type="button" value="修改fullName" @click="changeName">

        <span>{{fullName}}</span>

      </div>

      <script>

        // 创建 Vue 实例,得到 ViewModel

        var vm = new Vue({

          el: '#app',

          data: {

            firstName: 'jack',

            lastName: 'chen'

          },

          methods: {

            changeName() {

              this.fullName = 'TOM - chen2';

            }

          },

          computed: {

            fullName: {

              get: function () {

                return this.firstName + ' - ' + this.lastName;

              },

              set: function (newVal) {

                var parts = newVal.split(' - ');

                this.firstName = parts[0];

                this.lastName = parts[1];

              }

            }

          }

        });

      </script>

    ```

    ## `watch`、`computed`和`methods`之间的对比

    1. `computed`属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算。主要当作属性来使用;

    2. `methods`方法表示一个具体的操作,主要书写业务逻辑;

    3. `watch`一个对象,键是需要观察的表达式,值是对应回调函数。主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操作;可以看作是`computed`和`methods`的结合体;

    ## `nrm`的安装使用

    作用:提供了一些最常用的NPM包镜像地址,能够让我们快速的切换安装包时候的服务器地址;

    什么是镜像:原来包刚一开始是只存在于国外的NPM服务器,但是由于网络原因,经常访问不到,这时候,我们可以在国内,创建一个和官网完全一样的NPM服务器,只不过,数据都是从人家那里拿过来的,除此之外,使用方式完全一样;

    1. 运行`npm i nrm -g`全局安装`nrm`包;

    2. 使用`nrm ls`查看当前所有可用的镜像源地址以及当前所使用的镜像源地址;

    3. 使用`nrm use npm`或`nrm use taobao`切换不同的镜像源地址;

    ## 相关文件

    1. [URL中的hash(井号)](http://www.cnblogs.com/joyho/articles/4430148.html)

    Data多实例共享问题:

    app.vue文件我们可以分成三部分解读,

    <template></template>标签包裹的内容:这是模板的HTMLDom结构,里边引入了一张图片和<router-view></router-view>标签,<router-view>标签说明使用了路由机制。我们会在以后专门拿出一篇文章讲Vue-router。

    <script></script>标签包括的js内容:你可以在这里些一些页面的动态效果和Vue的逻辑代码。

    <style></style>标签包裹的css内容:这里就是你平时写的CSS样式,对页面样子进行装饰用的,需要特别说明的是你可以用<style scoped></style>来声明这些css样式只在本模板中起作用。

    相关文章

      网友评论

          本文标题:Vue基础

          本文链接:https://www.haomeiwen.com/subject/owndoqtx.html