vue全部笔记

什么是Vue.js

  • Vue.js是目前最火的一个前端框架,React是最流行的一个前端框架,(React除了开发网站,还可以开发手机App,Vue语法也是可以用于手机App开发的,需要借助于Weex)
  • Vue.js是前端的主流框架之一,和Angualr.js、React.js一起,并称为前端三大主流框架
  • Vue.js是一套构建用户界面的框架,**只关注视图层++,它不仅易上手,还便于与第三方库或既有项目整合。(Vue有配套的第三方类库,可以整合起来做大型项目的开发)
  • 前端的主要工作?主要负责MVC中的V这一层,蛀牙工作就是和界面打交道。

为什么学习流行框架

  • 企业为了提高开发效率,在企业中,时间就是效率,效率就是金钱。
  • 提高开发效率的发展历程:原生JS -> Jquery之类的类库 -> 前端模板引擎 -> Angular.js / Vue.js(能够帮助我们减少不必要的DOM操作;提高渲染效率;双向数据绑定的概念【通过跨年提供的指令,前端程序员只需要关心数据的业务逻辑,不在关心DOM是如何渲染的】)
  • 在Vue中,一个核心的概念,就是让用户不再操作DOM元素,解放了用户的双手,让程序员可以更多的时候去关注业务逻辑。
  • 增强就业竞争力

框架和库的区别

  • 框架:是一套完整的解决方案:对项目的侵入性较大,项目如果需要更换框架,则需要重新架构整个项目
  • 库(插件):提供某一个小功能,对项目的侵入性较小。如果某个库无法完成某些需求,可以很容易切换到其他库实现需求。

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

  • MVC是后端的分层开发概念。M指Model,主要处理数据;V是视图层,(前端页面);C是业务逻辑层(路由、用户登录注销)

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

  • 为什么有了MVC还要有MVVM

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

Vue之 基本代码结构插值表达式v-cloak

Vue指令之v-texv-html

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

  1. 直接使用 指令v-bind
  2. 使用简化指令:
  3. 在绑定的时候,拼接绑定内容::title="btnTitle + ',这是追加的内容'"

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

跑马灯效果

  1. HTML结构:

     <div id="app">
         <input type="button" value="快乐" @click="run">
         <input type="button" value="我不" @click="stop">
         <h4>{{ msg }}</h4>
     </div>
    

2.Vue实例

    //在vm实例中,如果要获取data里的数据或调用methods中的方法,
    //必须通过 this.数据属性名 或 this.方法名来进行访问,this代表vm实例对象
    var vm = new Vue({
        el: '#app',
        data: {
            msg: '快乐的一只小青蛙~',
            intervalId: null //在data上定义定时器的Id,开一个定时器就重新定义一遍
        },
        methods: {
            run() {
                
                // 判断定时器是否为null,是否在静止状态点击的“快乐”按钮,不是则返回,是则执行下列代码
                if(this.intervalId != null) return;

                //es6语法,不需要先获取this再重新赋值
                //嵌套函数内部的this与外部的this保持一致
                this.intervalId = setInterval( () => {
                    // 获取头部的第一个字符
                    var start = this.msg.substring(0,1)
                    // 获取到后面的所有字符
                    var end = this.msg.substring(1)
                    // 重新拼接得到新的字符串,并赋值给 this.msg
                    this.msg = end + start

                    //vm实例会监听自己内部data中所有数据的改变,只要数据已发生编发,就会自动把最新的数据,
                    //从data上同步到页面中去,因此程序员只要关心数据的变化,不需要关心重新渲染
                }, 400)
                
            },
            stop() {
                clearInterval(this.intervalId)
                // 每当清除了定时器之后,需要把定时器重新赋值给null
                this.intervalId = null
            }
        }
    })

    //分析
    //1. 给“快乐”按钮,绑定一个点击事件
    //2. 在按钮的事件处理函数中,写相关的业务逻辑:拿到msg字符串,然后低哦用字符串的
    //substring来进行字符串的截取操作,把第一个字符截取出来放到最后一个位置即可
    //3. 为了实现点击下按钮,自动截取的功能,需要把步骤2中国的代码,放到一个定时器中去。

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="+">+</option>
            <option value="-">-</option>
            <option value="*">*</option>
            <option value="/">/</option>
        </select>

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

        <input type="button" value="=" @click="calc">

        <input type="text" v-model="result">
        
    </div>
  1. vue实例

         var vm = new Vue({
         el: "#app",
         data: {
             n1: 0,
             n2: 0,
             result: 0,
             opt: '+'
         },
         methods: {
             calc () {
                 //1.算数方法
                 // switch(this.opt){
                 //     case '+':
                 //         this.result = parseInt(this.n1) + parseInt(this.n2)
                 //         break;
                 //     case '-':
                 //         this.result = parseInt(this.n1) - parseInt(this.n2)
                 //         break;
                 //     case '*':
                 //         this.result = parseInt(this.n1) * parseInt(this.n2)
                 //         break;
                 //     case '/':
                 //      this.result = parseInt(this.n1) / parseInt(this.n2)
                 //         break;
                 // }
                 
                 //2.投机取巧,正式开发中,尽量少用
                 var codeStr = 'parseInt(this.n1)' + this.opt + 'parseInt(this.n2)'
                 this.result = eval(codeStr)
             }
         }
     });
    

在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: {
   h1StyleObj1: { color: 'red', 'font-size': '40px', 'font-wight': '200'},
   h1StyleObj2: { fontStyle: 'italic' }
}
  • 在元素中,通过属性绑定的形式,将杨思对象应用到元素中:
<h1 :style="{h1StyleObj,h2StyleObj2}">这是一个熟悉的H1</h1>

Vue指令之v-forkey属性

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-ifv-show

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

Vue.js-Day2

品牌管理案例

添加新品牌

删除品牌

根据条件筛选品牌

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

[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>
  1. 在2.x版本中:

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

https://chrome.google.com/webstore/search/devtools?hl=zh-CN

过滤器

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

私有过滤器

  1. HTML元素
    <td>{{item.ctime | dataforms('yyyy-mmm-dd')}}</td>
  1. 私有filters定义方式:

     //定义一个私有的过滤器
     var vm2 = new Vue({
         el: '#app2',
         data: { 
             dt: new Date()
         },
         methods: { },
         //带 s ,对象, 定义私有过滤器, 过滤器有两个条件: [过滤器名称 和 处理函数]
         //过滤器调用的时候,采用的是就近原则,如果私有过滤器和全局过滤器的名称一样,则优先调用私有的
         filters: { 
             dataFormat: function(dateStr, pattern = '') {
         //根据给定的时间字符串,得到特定的时间
         var dt = new Date(dateStr)
    
         // yyyy - mm - dd
         var y = dt.getFullYear()
         //padStart(maxLength, fillString='')字符填充方法,最大长度为2,不够则在前面填充0
         //padEnd(maxLength, fillString='')同理,不同的是在后面添加
         var m = (dt.getMonth() + 1 ).toString().padStart(2,'0')
         var d = dt.getDate().toString().padStart(2,'0')
    
         //先转成小写
         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, fillSting='')来填充字符串;

全局过滤器

    //定义一个全局的过滤器, 进行时间格式化,被所有的vm实例共用
    Vue.filter('dataFormat',function (dateStr, pattern="") {
        //根据给定的时间字符串,得到特定的时间
        var dt = new Date(dateStr)

        // yyyy - mm - dd, 获取年月日
        var y = dt.getFullYear()
        var m = dt.getMonth() + 1 
        var d = dt.getDate()

        // return y + '-' + m +'-' +d //用以下形式代替:
        //return `${y}-${m}-${d}`

        //先转成小写
        //如果 传递进来的字符串类型 ,转为小写后,等于yyyy-mm-dd,那么就返回 年-月-日
        //否则,就返回 年-月-日 时:分:秒
        if(pattern.toLowerCase() === 'yyyy-mm-dd') {
            return `${y}-${m}-${d}`
        }else {
            //获取时分秒
            var hh = dt.getHours()
            var mm = dt.getMinutes()
            var ss = dt.getSeconds()

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

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

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

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

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

2.x自定义键盘修饰符 https://cn.vuejs.org/v2/guide/events.html#%E6%8C%89%E9%94%AE%E7%A0%81

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

`Vue.config.keyCodes.f2 = 113;`

1.2 使用自定义的按键修饰符

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

2.可以直接调用按键值(f2的按键值为113)

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

3.使用系统内置的按键可以不用定义

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

自定义指令 https://cn.vuejs.org/v2/guide/custom-directive.html

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

全局

    // 注册一个全局自定义指令 `v-focus`
    Vue.directive('focus', {
    // 当被绑定的元素插入到 DOM 中时……
    inserted: function (el) {
    // 聚焦元素
    el.focus()
    }
    })

局部

    directives: {
        focus: {
        // 指令的定义
        inserted: function (el) {
         el.focus()
        }
        }
    }
  1. 钩子函数

https://cn.vuejs.org/v2/guide/custom-directive.html#%E9%92%A9%E5%AD%90%E5%87%BD%E6%95%B0

    // 使用 Vue.derective() 自定义全局指令
    // 其中:参数1:指令的名称,注意,在定义的时候,指令的名称前面,不需要加 v- 前缀,
    // 但是: 在调用的时候,必须在指令名称前加上 v- 前缀来进行调用
    //参数2:是一个对象,这个对象身上,有一些指令相关的函数,这些函数可以在特定的阶段,执行相关的操作
    Vue.directive('focus',{
        bind:function(el) {//每当指令绑定到元素上的时候,就立即执行 bind 函数,只执行一次
            /// 注意: 在每个函数中,第一个参数永远是 el ,表示被绑定了指令的元素,这个 el 参数,是一个原生的JS对象
            // 在元素刚绑定了指令的时候,还没有插入到 DOM 中去,这时候,调用 focus 方法没有作用
            // 因为,一个元素,只有插入DOM之后,才能获取焦点
            //el.focus()
        },
        inserted: function(el) {// inserted 表示元素插入到 DOM 中的时候,会执行  inserted 函数,只执行一次
            el.focus()
            // 和 JS 行为有关的操作,最好在 inserted 中去执行,防止 JS 行为不生效
        },
        updated: function() {// 当 VNode 更新的时候,会执行 updated 函数,可能被触发多次

        }
    })
  1. 钩子函数的参数

https://cn.vuejs.org/v2/guide/custom-directive.html#%E9%92%A9%E5%AD%90%E5%87%BD%E6%95%B0%E5%8F%82%E6%95%B0

  • el:指令所绑定的元素
  • binding
  • name:指令的名字,不包含v-前缀
  • value:指令的绑定值:例如:v-my-directive="1 + 1" 中,绑定值为 2
  • expression:字符串形式的表达式,例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"

html:

<input type="text" class="form-control" v-model="keywords" id="search" v-focus v-color="'blue'">

自定义指令:

    //自定义设置字体颜色的指令
    Vue.directive('color',{
        // 样式只要通过指令绑定给了元素,不管这个元素有没有被插入到页面中去,这个元素肯定有一个内联样式
        // 将来元素肯定会显示到页面上,这时候浏览器的渲染引擎必然会解析样式,应用给这个元素
        bind:function(el,binding) {
            // el.style.color = 'red'
            // 和 样式 相关的操作,一般都可以在 bind 中定义

            el.style.color = binding.value
        }
    })
  1. 简写函数:在 bind 和 update 时触发相同行为,而不关心其它的钩子函数

下面的例子是自定义私有指令和简写函数

    directives: { // 自定义私有指令
       'fontweight': { // 设置字体粗细
           bind: function (el,binding) {
               el.style.fontWeight = binding.value
           }
        },
        'fontsize': function(el,binding) { //简写函数,注意:这个 function 等同于把代码写到了 bind 和 update 中去
             el.style.fontSize = parseInt(binding.value) + 'px'
        }
    }

Vue实例的生命周期

生命周期

  • 什么是生命周期:从Vue实例创建、运行、到销毁期间,总是伴随着各种各样的事件,这些事件,统称为生命周期。
  • 生命周期钩子:就是生命周期事件的别名而已;
  • 生命周期钩子 = 生命周期函数 = 生命周期事件
  • 主要的生命周期函数分类:
  • 创建期间的生命周期函数:
    • beforeCreate:实例刚在内存中被创建出来,此时,还没有初始化好 data 和 methods 属性
    • created: 实例已经在内存中创建完成;此时 data 和 methods 已经创建完成,此时还没有开始编译模板
    • beforeMount:此时已经完成了模板的编译,但是还没有挂载到页面中
    • mounted:此时,已经将编译好的模板,挂载到了页面指定的容器中显示
  • 运行期间的生命周期函数:
    • beforeUpdate:状态更新之前执行此函数,此时 data 中的状态 值是最新的,但是界面上显示的数据还是旧的。因为此时还没有开始重新渲染 DOM 节点
    • updated:实例更新完毕之后调用此函数,此时 data 中的状态值 和 界面上显示的数据,都已经完成了更新,界面已经被重新渲染好了
  • 销毁期间的生命周期函数:
    • beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用
    • destroyed:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解除绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。

vue-resource实现get,post,jsonp请求

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

  1. 之前的学习中,如何发起数据请求?
  2. 常见的数据请求类型?get,post,jsonp
  3. 测试的URL请求资源地址
  4. JSONP的实现原理
    • 由于浏览器的安全性限制,不允许AJAX访问协议不同、域名不同、端口号不同的数据接口,浏览器认为这种访问不安全;
    • 可以通过动态创建script标签的形式,把script标签的src属性,指向数据接口的地址,因为script标签不存在跨域限制,这种 数据获取方式,称作为JSONP(注意:根据JSON的实现原理,知晓,JSONP只支持GET请求);
    • 具体实现过程;
      • 先在客户端定义一个回调方法,预定义对数据的操作;
      • 再把这个回调方法的名称,通过URL传参的形式,提交都服务器的数据接口
      • 服务器数据接口组织好要发送给 客户端的数据,再拿着客户端传递过来的回调方法名称,拼接出一个调用这个方法的字符串,发送给客户端去解析执行;
      • 客户端拿到服务器返回的字符串之后,当作script脚本去解析执行,这样就能够拿到JSONP的数据了;

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

  1. 先解压安装PHPStudy
  2. 解压安装Navicat这个数据库可视化工具,并激活;
  3. 打开Navicat工具,新建空白数据库,名为dtcmsdb4
  4. 双击新建的数据库,连接上这个空白数据库,在新建的数据库上右键->运行SQL文件,选择并执行dtcmsdb4.sql这个数据库脚本文件;如果执行不报错,则数据库导入完成;
  5. 进入文件夹vuecms3_nodejsapi内部,执行npm i安装所有的依赖项;
  6. 先确保本机安装 了nodemon,没有安装则运行npm i nodemon -g进行全局安装,安装完毕后,进入到vuecms_nodejsapi目录 ->src目录 -> 双击运行 start.bat
  7. 如果API启动失败,请检查 PHPStudy 中默认的用户名是 root,默认的密码也是 root

品牌管理改造

展示品牌列表

添加品牌数据

删除品牌数据

Vue中的动画

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

使用过渡类名

  1. HTML结构

     <div id="app>
      <input type="button" value="动起来" @click="myAnimate">
      <!-- 使用 transition 将需要过渡的元素包裹起来 -->
      <transition name="false">
     <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;
             }
         }
     }) 
    

Vue.js-Day3

定义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.js - Day4

父组件向子组件传值

  1. 组件实例定义方式,注意:一定要使用props属性要定义父组件传递过来的数据
    <script>
    // 创建 Vue 实例,得到 ViewModel
    var vm = new Vue({
        el:'#app',
        data: {
            msg:'这是父组件的消息'
        },
        components: {
            son: {
                template: '<h1>这是子组件 --- {{finfo}}</h1>',
                props: [finfo]
            }
        }
    });
    </script>
  1. 使用v-bind或简化指令,将数据传递到子组件中:

     <div id="app">
         <son :finfo="msg"></son>
     </div>
    

子组件向父组件传值

  1. 原理:父组件将方法的引用,传递到子组件内部,子组件在内部调用父组件传递过来的方法,同时把要发送给父组件的数据,在调用方法的时候当作参数传递进去;
  2. 父组件将方法的引用传递给子组件,其中,getMsg是父组件中methods中定义的方法名称,funo是子组件调用传递过来方法时候的方法名称
    <son @funo="getMsg"></son>
  1. 子组件内部通过this.$emit(‘方法名',要传递的数据)方式,来调用父组件中的方法,同时把数据传递给父组件使用
    <div id="app">
        <!-- 引用父组件 -->
        <son @funo="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('funo,'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="getElemrnt" />
        <!-- 使用 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 Vuew({
        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-linx 组件来导航

     <!-- 2. 使用 router-link 组件来导航 -->
     <router-link to="/login">登录<、router-link>
     <router-link to="/register">注册<、router-link>
    
  3. 使用 touter-view 组件来显示匹配到的组件

     <!-- 3. 使用 touter-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 实例,通过 router 属性来定义路由匹配规则

     // 5. 创建一个路由 router 实例,通过 router 属性来定义路由匹配规则    
     var router = new VueRouter({
         router: {
             { path: '/login',component: login },
             { path: '/register',component: register }
         }
     });
    
  6. 使用 router 属性来使用路由规则

     // 6. 创建vue实例,得到ViewModel
     var vm = new Vue({
         el:'#app',
         router: router // 使用router属性来使用路由规则
     });
    

使用tag属性指定router-link渲染的标签类型

    <!-- router-link 默认渲染为一个 a 标签 -->
    <!-- tag 属性指定 router-link 渲染为 什么元素 -->
    <!-- 不管渲染为什么元素,都是可点击的元素 -->
    <router-link to="/login" tag="span">登录</router-link>
    <router-link to="/register">注册</router-link>

设置路由重定向

    // 2. 创建一个路由对象,当导入 vue-router 包之后,在 winodow 全局对象中,就有了一个 路由的构造函数:VueRouter
    // 在 new 路由对象的时候,可以为构造函数,传递一个配置对象
    var routerObj = new VueRouter({
        // route //这个配置对象中的 route 表示 【路由匹配规则】 的意思
        routes: [ // 路由匹配规则
        //每个路由规则,都是一个对象,这个规则对象身上,有两个必须的属性:
        // 属性1 是 path,表示监听哪个路由链接地址
        // 属性2 是 component,表示如果路由前面匹配到的 path,则展示 component 属性对应的那个组件
        // 注意:component 的属性值,必须是一个 组件模板对象,不能是 组件的引用名称('login')
            { path: '/', redirect: '/login'}, //如果请求的是默认路径,则重定向到 login 组件中
            { path: '/login', component: login},
            { path: '/register', component: register}
        ],
        linkActiveClass: 'myactive' // 为路由高亮指定类名,高亮时使用 .myactive 样式,不会使用默认的 .router-link-active 样式
    })

设置路由高亮

方式一:使用vue提供的.router-link-active 进行定义

    /* 实现路由高亮 */
    .router-link-active {
        color: red;
        font-weight: 800;
        font-style: italic;
        font-size: 80px;
        text-decoration: underline;
    }

方式二:为路由对象linkActiveClass重新设置一个自定义类名,这个类名可以是bootstrap的类名也可以是自定义的

linkActiveClass: 'myactive'

    .myactive {
        color: red;
        font-weight: 800;
        font-style: italic;
        font-size: 80px;
        text-decoration: underline;
        background-color: brown;

设置路由切换动效

    <!-- 这是 vue-router 提供的元素,专门用来当做占位符的,将来,路由规则匹配到的组件,就会展示到这个 router-view 中去 -->
    <transition mode="out-in"> <!-- 添加动画效果 -->
        <router-view></router-view>
    </transitio

    .v-enter,
    .v-leave-to {
        opacity: 0;
        transform: translateX(140px);
    }

    .v-enter-active,
    .v-leave-active {
        transition: all 0.5s ease;
    }

在路由规则中定义参数

方式一:

  1. 在规则中定义参数:

     { path: 'register/:id/:name', component: register }
    
  2. 在路由中传递参数:(12、zhangsan 分别对应规则中的id、name)

     <router-link to="/login/12/zhangsan">注册</router-link>
    
  3. 通过this.$router.params来获取路由中的参数:(this可省略)

     var register = Vue.extend({
         template: '<h1>注册组件 --- {{ this.$router.params.id --- {{ this.$router.params.name }}</h1>'
     });
    

方式二:

  1. 直接在路由中传递参数

     <!-- 如果在路由中,使用 查询字符串,给路由传参,则不需要修改路由规则的 path 属性 -->
     <router-link to="/login?id=10&name=zhangsan">登录</router-link>
    
  2. 通过this.$router.query来获取路由中的参数:

     var login = {
         template: '<h1>登录 --- {{ $route.query.id }} --- {{ $route.query.name }}</h1>',
         data() {
             return {
                 msg: '123'
             }
         },
         created() {
             // 组件的生命周期钩子函数
             console.log(this.$route)
             console.log(this.$route.query.id)
         }
     }
    

使用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 }
                ]
            }
        ]
    });

     var vm = new Vue({
        el : '#app',
        data : {  },
        methods : { },
        components: {
            account
        },
        router: router    
    });
</script>

命名视图实现经典布局

  1. 标签代码结构:

     <div id="app">
    
         <router-view></router-view>
         <div class="container">
             <!-- name 属性指定要放的组件 -->
             <router-view name="left"></router-view>
             <router-view name="main"></router-view>
         </div>
    
     </div>
    
  2. JS代码

     <script>
    
     var header = {
         template: '<h1 class="header">Header头部</h1>'
     }
    
     var leftBox = {
         template: '<h1 class="left">Left侧边栏区域</h1>'
     }
    
     var mainBox = {
         template: '<h1 class="main">mainBox主体区域</h1>'
     }
    
     // 创建路由对象
     var router = new VueRouter({
         routes: [
             // { path: '/', component: header },
             // { path: '/left', component: leftBox },
             // { path: '/main', component: mainBox },
    
             { path: '/', components: {
                 'default': header,
                 'left': leftBox,
                 'main': mainBox
             } 
             },
    
         ]
     })
    
      var vm = new Vue({
         el : '#app',
         data : {  },
         methods : { },
         router    
     });
     </script>
    
  3. CSS代码:

     <style>
     html,body {
         margin: 0;
         padding: 0;
     }
    
     .header {
         height: 80px;
         background-color: orange;
     }
    
     h1 {
         margin: 0;
         padding: 0;
         font-size: 16px;
     }
    
     .container {
         display: flex;
         height: 600px;
     }
    
     .left {
         background-color: lightgreen;
         flex: 2;
     }
    
     .main {
         background-color: lightpink;
         flex: 8;
     }
     </style>
    

watch属性的使用

html结构

<div id="app">

    <input type="text" v-model="firstname"> +
    <input type="text" v-model="lastname"> =
    <input type="text" v-model="fullname">

</div>

JS代码

<script>
     var vm = new Vue({
        el : '#app',
        data : { 
            firstname: '',
            lastname: '',
            fullname: ''
         },
        methods : { },
        watch: {
            // 使用这个属性可以监视 data 中指定数据的变化,然后触发 watch 中对应的 function 处理函数
            // 监视 firstname 的变化,改变了则调用 function 函数,如果first-name(带横线),需要加上引号
            'firstname': function(newVal, oldVal) {
                // console.log('监视到了 firstname 的变化')
                // this.fullname = this.firstname + '-' + this.lastname

                // console.log(newVal + '---' + oldVal)

                this.fullname = newVal + '-' + this.lastname

            },
            'lastname': function(newVal) {
                this.fullname = this.firstname + '-' + newVal
            }
        }    
    });
</script>

使用watch监视路由地址的改变

html结构

<div id="app">

    <!-- 6.链接 -->
    <router-link to="/login">登录</router-link>
    <router-link to="/register">注册</router-link>

    <!-- 5.容器 -->
    <router-view></router-view>
</div>

JS代码

<script>
    // 2.创建子组件
    var login = {
        template: '<h1>登录组件</h1>'
    }

    var register = {
        template: '<h1>注册组件</h1>'
    }

    // 3.创建一个路由对象
    var router = new VueRouter({
        routes: [ //路由规则数组
            { path: '/', redirect: '/login' },// 默认路径重定向
            { path: '/login', component: login},
            { path: '/register', component: register}
        ],
        linkActiveClass: 'myactive' // 激活相关的类
    })

     var vm = new Vue({
        el : '#app',
        data : {  },
        methods : { },
        // 4. 挂载路由
        router,
        watch: {
            // this.$route.path 获取路由
            '$route.path': function(newVal,oldVal) {
                console.log( newVal + '---' + oldVal )
                if ( newVal === '/login') {
                    console.log('欢迎进入登录页面')
                } else if ( newVal === '/register') {
                    console.log('欢迎进入注册页面')
                }
            }
        }    
    });
</script>

computed计算属性的使用

html结构

<div id="app">

    <input type="text" v-model="firstname"> +
    <input type="text" v-model="lastname"> =
    <input type="text" v-model="fullname">

    <p>{{ fullname }}</p>
    <p>{{ fullname }}</p>
    <p>{{ fullname }}</p>

</div>

JS代码

<script>
     var vm = new Vue({
        el : '#app',
        data : { 
            firstname: '',
            lastname: ''
         },
        methods : { },
        computed: {
            // 在 computed 中,可以定义一些属性,这些属性叫做【计算属性】,
            //本质就是一个方法,只不过在使用这些计算属性的时候,
            //是把他们的 名称直接当做 属性来使用,并不会把计算属性当做方法去调用。

            // 1.注意:计算属性,在引用的时候,一定不要加()调用 ,直接把它当做普通属性去使用
            // 2.注意:只要计算属性这个 function 内部所用到的 data 中的数据发生了变化,就会立即重新计算这个计算属性的值
            // 意思是 fullname 永远跟随 firstname 和 lastname 的变化而变化
            // 3.注意:计算属性的求值结果,会被缓存起来,方便下次调用,如果 计算属性方法中,所有的数据都没有发生变化,就不会重新对计算属性求值
            // 'fullname': function() {
            //     console.log('ok')
            //     return this.firstname + '-' + this.lastname
            // }

            fullname: {
                get:function() {
                    console.log('ok')
                    return this.firstname + '-' + this.lastname
                },
                set:function() {}
            }
        }    
    });
</script>

watchcomputedmethods之间的对比

  1. computed属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算,主要当作属性来使用;
  2. methods方法表示一个具体的操作,主要书写业务逻辑;
  3. watch一个对象,键是需要观察的表达式,值是对应回调函数,主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操作,可以看作是computedmethods的结合体;

nrm的安装使用

作用:提供了一些最常用的NPM包镜像地址,能够让我们快速的切换安装包时候的服务器地址;
什么是镜像:原来包刚一开始是只存在于国外的NPM服务器,但是由于网络原因,经常访问不到,这时候,我们可以在国内,创建一个和官网完全一样的NPM服务器,只不过,数据都是从人家那里拿过来的,除此之外,使用方式完全一样。

  1. 运行npm i nrm -g全局安装nrm包;
  2. 使用nrm ls查看当前所有可用的镜像源地址以及当前所使用的镜像源地址;
  3. 使用nrm use npmnrm use taobao切换不同的镜像源地址;

注意:nrm只是单纯提供了几个常用的下载包的URL地址,并能够让我们在这几个地址之间,很方便的进行切换,但是,我们每次装包的时候,使用的装包工具都是npm

相关文件

URL中的hash(#号)

键盘对应的码值

Vue.js - Day5 - Webpack

在网页中会引用哪些常见的静态资源?

  • JS
  • .js .jsx .coffee .ts(TypeScript 类C#语言)
  • CSS
  • .css .less .sass .scss
  • Images
  • .jpg .png .gif .bmp .svg
  • 字体文件(Fonts)
  • .svg .ttf .eot .woff .woff2
  • 模板文件
  • .ejs .jade .vue[这是在webpack中定义组件的方式,推荐]

网页中引入的静态资源多了以后有什么问题?

  1. 网页加载速度慢,因为我们要发起很多的二次请求;
  2. 要处理错综复杂的依赖关系

如何解决上述两个问题

  1. 合并、压缩、精灵图、小图片的Base64编码
  2. 可以使用requireJS。也可以使用webpack,可以解决各个包之间的复杂依赖关系

什么是webpack

webpack 是前端的一个项目构建工具,它是基于node.js开发出来的一个前端工具,因此要使用webpack必须先安装node

如何完美实现上述的2种解决方案

  1. 使用Gulp,是基于task任务的;
  2. 使用Webpack,是基于整个项目进行构建的;
  • 借助于webpack这个前端自动化构建工具,可以完美实现资源的合并、打包、压缩、混淆等诸多功能。
  • 根据官网的图片介绍webpack打包过程
  • webpack官网

webpack 安装的两种方式

  1. 运行npm i webpack -g全局安装webpack,这样就能在全局使用webpack的命令
  2. 在项目根目录中运行npm i webpack --save-dev安装到项目依赖中

初步使用webpack打包构建列表隔行变色案例

  1. 运行npm init初始化项目,使用npm管理项目中的依赖包

VScode快捷键ctrl + · 打开终端,注意那个点是键盘上 esc 下面的那个;运行npm init -y初始化,初始化完成后可以想见项目目录多了package.json文件

    E:\Vue_Project\vue-study\day5\webpack-study> npm init -y
    Wrote to E:\Vue_Project\vue-study\day5\webpack-study\package.json:

    {
        "name": "webpack-study",
        "version": "1.0.
        "description": "",
         "main": "main.js",
        "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
    },
    "keywords": [],
    "author": "",
    "license": "ISC"
    }
  1. 创建项目基本的目录结构
  2. 使用cnpm i jquery --save安装jquery类库

运行npm i jquery -S安装jquery

    E:\Vue_Project\vue-study\day5\webpack-study> npm i jquery -S
    npm notice created a lockfile as package-lock.json. You should commit this file.
    npm WARN webpack-study@1.0.0 No description
    npm WARN webpack-study@1.0.0 No repository field.

    + jquery@3.4.1
    added 1 package from 1 contributor and audited 1 package in 2.161s
    found 0 vulnerabilities
  1. 创建main.js并书写各行变色的代码逻辑:

     // 导入jquery类库
     import $ from 'jquery'
     
     // 设置偶数行背景色,索引从0开始,0是偶数
     $('#list li:even').css('backgroundColor','lightblue');
     // 设置奇数行背景色
     $('#list li:odd').css('backgroundColor','pink');
    
  2. 直接在页面上引用main.js会报错,因为浏览器不认识import这种高级的JS语法。需要使用webpack进行处理,webpack默认会把这种高级的语法转换为低级的浏览器能识别的语法;

运行webpack 入口文件路径 输出文件路径main.js进行处理:

    webpack src/js/main.js dist/bundle.js

注意:如果webpack版本过高,上述语句会报错,需要用webpack .\src\main.js -o .\dist\bundle.js来打包,详情webpack 打包报错:Can't resolve '.\dist\bundle.js' in 'E:\vivian....'

  1. 由于每次修改main.js文件都需要重新使用webpack src/js/main.js dist/bundle.js进行打包,累赘且麻烦,因此在项目根目录引入配置文件webpack.config.js,声明要打包的文件路径和导出的文件路径,下次项目更改需要重新打包时,运行webpack即可。

webpack.config.js配置文件内容:

const path = require('path')

// 这个配置文件,其实就是一个 JS 文件,通过 node 中的模块操作,向外暴露了一个 配置对象
module.exports = {
    mode: 'development',
    entry: path.join(__dirname, './src/main.js'), // 入口,表示要使用 webpack 打包那一个文件
    output: {
        //输出文件相关配置
        path: path.join(__dirname, './dist'),//指定 打包好的文件,输出到哪个目录中去
        filename: 'bundle.js' // 指定 输出文件的名称
    }
}

// 当我们在控制台,直接输入 webpack 命令执行的时候,webpack做了以下几步:
// 1. 首先,webpack发现我们并没有通过命令的形式,给它指定出口和入口
// 2. webpack 就会去 项目的根目录 中,查找一个叫做'webpack.config.js'的配置文件
// 3. 当找到配置文件后,webpack 会去解析执行这个 配置文件,当解析执行完配置文件后,就得到了配置文件中导出的 配置对象
// 4. 当 webpack 拿到配置对象后,就拿到了配置对象中,指定的 入口 和出口,然后进行打包构建
  1. 再由于每次代码更改都需要手动重新打包编译,使用webpack-dev-server这个工具进行偷懒。运行npm i webpack-dev-server -D,把这个工具安装到项目的本地开发依赖。

  2. webpack-dev-server要求webpack安装在本地项目下,运行npm i webpack -D安装。

  3. webpack要求webpack-cli安装在本地项目下,运行npm i webpack-cli -D安装。

  4. package.json内,添加dev

    {
    "name": "webpack-study",
    "version": "1.0.0",
    "description": "",
    "main": "main.js",
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "webpack-dev-server" //添加webpack-dev-server
    },
    
  5. 运行npm run dev使用webpack-dev-server工具。

  6. i 「wds」: Project is running at http://localhost:8080/ 可在终端查看到服务器端口http://localhost:8080/
    i 「wds」: webpack output is served from /可在终端查看到打包的bundle.js的路径是根目录下的绝对路径,可以通过http://localhost:8080/bundle.js访问到bundle.js。此时引入bundl.js只需写根目录的路径即可<script src="/bundle.js"></script>

  7. 此时修改代码只需保存,webpack-dev-server就会实时监听代码的改变和刷新浏览器,不需要再手动打包,甚至不用刷新浏览器即可看到效果。

webpack-dev-server 帮我们打包生成的 bundle.js 文件,并没有存放到实际的物理磁盘中,而是直接托管到了电脑的内存中,所以,我们在项目的根目录中,找不到打包好的bundle.js。 可以认为,webpack-dev-server 把打包好的文件,以一种虚拟的形式,托管到了项目的根目录中,虽然我们看不见,但是可以认为,和 dist src node_modules平级,有一个看不见的bundle.js文件。正是这种虚拟的托管方式,大大提高了加载效率。

  1. 此时需要手动打开浏览器并且是固定端口"8080",且进入的页面是项目根目录,十分不爽,因此修改package.json代码:"dev": "webpack-dev-server --open --port 3000 --contentBase src"open意思是自动打开浏览器并运行项目,port 3000意思是在3000端口下运行,contentBase src的意思是打开时默认打开src目录下的页面。

  2. 继续修改package.json代码:"dev": "webpack-dev-server --open --port 3000 --contentBase src --hot"hot是热更新的意思,意思是将修改的那一部分代码作为补丁打在打包生成的bundle.js上,实现局部更新,加快加载进度。

  3. 以上步骤完成后,运行项目只需在项目根目录下运行npm run dev命令,webpack-dev-server就会帮我们自动打开浏览器并在我们指定的端口下运行我们指定的页面,修改代码后仅需保存webpack-dev-server就会帮助我们自动刷新浏览器。

配置 dev-server 的第二种形式

在webpack.config.js下添加

// 启用热更新的第二步
const webpack = require('webpack')

devServer: { // 这是配置 dev-server 命令参数的第二种形式,相对来说,这种方式麻烦一些
    // --open --port 3000 --contentBase src --hot
    open: true, // 自动打开浏览器
    port: 3000, // 设置启动时的运行端口
    contentBase: 'src', // 指定托管的根目录
    hot: true // 启动热更新的第一步
}

plugins: [ // 配置插件的节点
    new webpack.HotModuleReplacementPlugin() // new 一个热更新的模块的对象,启用热更新的第3步
]

在 package.json 中 只需声明"dev": "webpack-dev-server"

使用help-webpack-plugin插件配置启动页面

由于使用--contentBase指令的过程比较繁琐,需要绑定启动的目录,同时还需要修改index.html中script标签的src属性,所以推荐大家使用html-webpack-plugin插件配置启动页面。

这个插件的两个作用:1. 自动在内存中根据指定页面生成一个内存中的页面。2. 自动把打包好的 bundle.js 追加到页面中去

  1. 运行cnpm i html-webpack-plugin --save-dev安装到开发依赖

  2. 修改webpack.config.js配置文件如下:

     // 导入处理路径的模块
     var path = require('path');
     // 导入自动生成HTML文件的插件
     var htmlWebpackPlugin = require('html-webpack-plugin');
     
     module.exports = {
         entry: path.resolve(_dirname, 'src/js/main.js'), // 项目入口文件
         output: { // 配置输出选项
             path: path.resolve(_dirname, 'dist'), // 配置输出的路径
             filename: 'bundle.js' // 配置输出的文件名
         },
         plugins: [ // 添加plugins节点配置插件
             new htmlWebpackPlugin({
                 template:path.resolve(_dirname,'src/index.html'), // 模块路径
                 filename: 'index.html' // 自动生成的HTML文件的名称
             })
         ]
     }
    
  3. 修改package.jsonscript节点中的dev指令如下:
    "dev": "webpack-dev-server"

  4. 将index.html中script标签注释掉,因为html-webpack-plugin插件会自动把bundle.js注入到index.html页面中。此时查看页面源代码可以看到底部已经自动引用了bundle.js

使用webpack打包css文件

  1. 运行cnpm i style-loader css-loader --save-dev

  2. 修改webpack.config.js文件

     module: { // 用来配置第三方loader模块的
         rules: [ // 文件的匹配规则
             { test: /\.css$/,use: ['style-loader', 'css-loader']} // 处理css文件的规则 
         ]
     }
    
  3. 注意:css表示使用哪些模块来处理test所匹配到的文件;use中相关loader模块的调用顺序是从后向前调用的;

使用webpack打包less文件

  1. 运行cnpm i less-loader less -D
  2. 修改webpack.config.js这个配置文件:
    { test: /\.less$/,use: ['style-loader', 'css-loader','less-loader']},

使用webpack打包scss文件

  1. 运行npm i sass-loader -D,终端提示需要安装node-sass依赖

  2. 运行cnpm i node-sass -D(注意:只有极少数情况才能使用npm成功安装node-sass,所以这里推荐使用cnpm安装)

  3. 修改webpack.config.js这个配置文件:
    { test: /\.scss$/,use: ['style-loader', 'css-loader','sass-loader']},

  4. 本人在运行npm run dev命令后报错ERROR in ./src/css/index.scss (./node_modules/css-loader/dist/cjs.js!./node_modules/_sass-loader@7.3.1@sass-loader/dist/cjs.js!./src/css/index.scss),百度了一下问题解决方法webpack打包node-sass编译报错,可行,运行成功。

// 注意: webpack 处理第三方文件类型的过程:
// 1. 发现这个要处理的文件不是 JS 文件,然后就去配置文件中查找有没有对应第三方 loader 规则
// 2. 如果能找到对应的规则,就会调用对应的 loader 处理这种文件类型;
// 3. 在调用 loader 的时候,是从后往前调用的
// 4. 当最后的一个 loader 调用完毕,会把处理的结果交给 webpack 打包合并,最终输出到 bundle.js 中去

使用webpack识别url地址

  1. 运行npm i url-loader file-loader -D命令,安装url-loader第三方模块,同时该模块依赖file-loader,因此一起安装了。

  2. 修改webpack.config.js这个配置文件:{ test: /\.(jpg|png|gif|bmp|jpeg)$/,use: 'url-loader'}

  3. 可以通过传参来指定多大的图片才转码。比如有一张图片是40062字节的,此时修改{ test: /\.(jpg|png|gif|bmp|jpeg)$/,use: 'url-loader?limit=40063'},limit 给定的值是图片的大小,单位是 byte,如果图片大于给定的 limit 值,则不会被转为 base64 格式的字符串,如果图片小于或等于给定的 limit,则会被转为 base64 字符串

     图片大小40062 = limit值40062 转
     图片大小40062 > limit值40061 不转
     图片大小40062 < limit值40063 转
    
  4. 可以通过传参来指定不转码时图片的名称。修改{ test: /\.(jpg|png|gif|bmp|jpeg)$/,use: 'url-loader?limit=40061&name=[name].[ext]'}[name]指图片原来的名字叫什么就是什么,不会被改变,[ext]指图片原来的格式不会被改变。

  5. 经过上一步,图片不转码时名称不会被改变,但是当引用了两张一样名字的图片时,先引用的图片会被后引用的覆盖,此时修改{ test: /\.(jpg|png|gif|bmp|jpeg)$/,use: 'url-loader?limit=40061&name=[hash:8]-[name].[ext]'}[hash:8]-表示图片前面添加8位的哈希值,最高可添加32位。这样就能避免同名图片被覆盖。

webpack使用url-loader处理字体文件

  1. 运行cnpm i bootstrap -S安装bootatrap。
  2. 在webpack.config.js内添加{ test: /\.(ttf|eot|svg|wpff|woff2)$/,use: 'url-loader' },在index.js添加import 'bootstrap/dist/css/bootstrap.css'

组件中的css作用域问题

抽离路由器为单独的模块

使用 饿了么的MintUI组件

Github仓储地址

Mint-UI官方文档

完整引入

  1. 导入所有的MintUI组件
    import MintUI from 'mint-ui'
  1. 导入样式表:
    import 'mint-ui/lib/style.css'
  1. 将Mint-UI注册到 Vue 身上
    Vue.use(MintUI)
  1. vue 中使用 MintUI 中的 Button 按钮,使用例子:
    <mt-button type="primary" size="large">primary</mt-button>

完整引入之后,使用css组件只需导入标签即可
<mt-button type="default">default</mt-button>

vue中使用 Mint-UI的js组件

  1. 导入 js 组件(以 Toast弹框提示 为例)
    import { Toast } from 'mint-ui';

  2. 暴露接口

     export default {
         data() {
             return {}
         },
         methods: {
             show() {
                 Toast("提示信息");
             }
         }
     }
    
  3. 在 html 中添加点击事件
    <mt-button type="default" @click="show">default</mt-button>

webpack中bootstrap图标 不显示

Mint-UI中按需导入的配置方式

  1. 安装 babel-plugin-component:

npm install babel-plugin-component -D

  1. 修改.babelrc

     {
         "presets": [
             ["es2015", { "modules": false }]
         ],
         "plugins": [["component", [
         {
             "libraryName": "mint-ui",
             "style": true
         }
         ]]]
     }
    
  2. 按需导入 Mint-UI组件

import { Button } from 'mint-ui'

  1. 使用 Vue.compoent 注册 按钮组件

Vue.component('mybtn',Button)

Vue.component(Button.name,Button)

  1. 在相对应的vue文件中添加标签

<mybtn></mybtn>或直接引用<mt-button type="default" @click="show">default</mt-button>

使用 MUI 代码片段

注意:MUI不同于 Mint-UI,MUI 只是开发出来的一套好用的代码片段,里面提供了配套的样式,配套的HTML代码段,类似于 Bootstrap;而Mint-UI,是真正的组件套,是使用 Vue 技术封装出来的 成套的组件,可以无缝的和 VUE 项目进行集成开发;

因此,从体验上来说,Mint-UI体验更好,因为这是别人帮我们开发好的现成的Vue组件;

从体验上来说,MUI 和 Bootstrap 类似;

理论上,任何项目都可以使用 MUI 或 Bootstrap,但是,Mint-UI只适用于 Vue 项目;

官网首页

文档地址

  1. 导入 MUI 的样式表:
    import '../lib/mui/css/mui.min.css'
  1. webpack.config.js中添加新的 loader 规则:
    { test:/\.{png|jpg|gif|ttf}$/,use:'url-loader'}
  1. 根据官方提供的文档和example,尝试使用相关的组件

将项目源码托管到oschina中

  1. 点击头像 -> 修改资料 -> SSH公钥 如何生成SSH公钥

  2. 创建自己的空仓库,使用 git config --global user.name "用户名"git config --global user.email **@**.com来全局配置提交时用户的名称和邮箱

  3. 使用git init在本地初始化项目

  4. 使用touch README.mdtouch .gitignore来创建项目的说明文件和忽略文件;

  5. 使用git add .将所有文件托管到git中

  6. 使用git commit -m "init project"将项目进行本地提交

新建项目步骤

  • 新建项目文件夹-用vscode打开

  • 初始化项目:在终端运行“npm init”,生成“package.json”

  • 在项目根目录下新建文件夹“src”,和“dist”,在“src”目录下新建“main.js"和”index.html"

  • 安装“webpack-dev-server”,在终端输入“npm i webpack-wdev-server"

  • webpack-dev-server要求webpack安装在本地项目下,在终端执行命令"npm i webpack",webpack要求webpack-cli安装在本地项目下,执行命令"npm i webpack-cli"

  • 在根目录下新建“webpack.config.js",配置webpack。

      module.exports = {
      mode: 'development',
      entry: path.join(__dirname, './src/main.js'), // 入口,表示要使用 webpack 打包那一个文件
      output: {
          //输出文件相关配置
          path: path.join(__dirname, './dist'),//指定 打包好的文件,输出到哪个目录中去
          filename: 'bundle.js' // 指定 输出文件的名称
      }
      }
    
  • 在“package.json”文件下添加webpack-dev-server工具

    "dev": "webpack-dev-server --open --port 3000 --contentBase src --hot"

  • 运行"npm i html-webpack-plugin -D"安装插件自动生成bundle.js

  • 在配置文件webpack.config.js中添加

    var htmlWebpackPlugin = require('html-webpack-plugin')

  • 在配置文件webpack.config.js中添加配置节点

      //所有webpack插件的配置爱节点
      plugins: [
      new htmlWebpackPlugin({
          template:path.resolve(__dirname,'src/index.html'), // 模块路径
          filename: 'index.html' // 自动生成的HTML文件的名称
      })
      ]
    
  • 运行"npm run dev"不再报错。

配置css文件步骤

  • 运行"npm i style-lloader css-loader"安装插件,在webpack.config.js文件下添加配置节点

      module: { // 用来配置第三方loader模块的
      rules: [ // 文件的匹配规则
          { test: /\.css$/,use: ['style-loader', 'css-loader']} // 处理css文件的规则 
      ]
      }
    
  • 在main.js中引入css文件:import './css/index.css'

Vue.js - day6

注意:

有时候使用npm i node-sass -D装不上,这时候,就必须使用cnpm i node-sass -D

在普通页面中使用render函数渲染组件

在webpack中配置.vue组件页面的解析

  1. 运行 cnpm i vue -S将Vue安装为运行依赖;
  2. 运行cnpm i vue-loader vue-template-compiler -D将解析转换vue的包安装为开发依赖;
  3. 运行cnpm i style-loader css-loader -D将解析转换css的包安装为开发依赖,因为.vue文件中会写css样式;
  4. webpack.config.js中,添加module规则:
    { test:/\.js$/, use: 'babel-loader', exclude:/node_modules/ },

在使用webpack构建的Vue项目中使用模板对象

  1. webpack.config.js中添加resolve属性:
    resolve: {
        alias: {
            'vue$': 'vue/dist/vue.esm.js'
        }
    }
  1. 在 main.js文件下导入模板对象

import Vue from 'vue'

使用第三方loader处理高级语法

// 在 webpack 中,默认只能处理一部分 ES6 的新语法,一些更高级的 ES6 语法或者 ES7 语法,
// webpack 是处理不了的,这时候,就需要借助于第三方的 loader,来帮助 webpack 处理这些高级的语法
// 当第三方 loader 把高级的语法转为低级的语法之后,会把结果交给 webpack 去打包到 bundle.js 中

通过 babel,可以帮我们将高级的语法转为 低级的语法

  1. 在 webpack中,可以运行如下命令,安装两套包,去安装 babel 相关的 loader功能:
  • 1.1 第一套包: npm i babel-core babel-loader babel-plugin-transform-runtime -D
  • 1.2 第二套包: npm i babel-preset-env babel-preset-stage-0 -D
  1. 打开 webp 的配置文件,在 module 节点下的 rules 数组中,添加一个 新的匹配规则:
  • 2.1 { test:/.js$/, use: 'babel-loader', exclude:/node_modules/ }
  • 2.2 注意: 在配置 babel 的 loader 规则的时候,必须把 node_modules 目录,通过 exclude选项排除掉,原因如下:
    • 2.2.1 如果不排除 node_modules,则 babel 会把 node_modules 中所有的第三方 js 文件都打包编译,消耗cpu,打包速度变慢
    • 2.2.2 如果 babel 把 node_modules 中的js转换完毕,项目也无法正常运行
  1. 在项目的根目录中,新建一个 .babelrc 的babel 配置文件,这个配置文件,属于 JSON 格式,所以必须符合 JSON 的语法规范
  • 3.1 在 babelrc 中 写如下的配置:

         { 
             "presets": ["env","stage-0"],//语法
             "plugins": ["transform-runtime"] //插件
         }
    
  1. 目前安装的 babel-preset-env 是比较新的es语法插件

webpack 中如何使用 vue

  1. 安装 vue的包: cnpm i vue -S
  2. 由于在 webpack 中,推荐使用 .vue 这个组件模板文件定义组件,所以,需要安装能解析这种文件的 loader:cnpm i vue-loader vue-template-complier -D
  3. 在 main.js 中,导入 vue 模板 :import Vue from 'vue'
  4. 定义一个 .vue 结尾的组件,其中,组件由三部分组成: template script style
  5. 使用 import login from './login.vue'导入这个组件
  6. 创建 vm 实例,var vm = new Vue({ el: 'app',render: c => c(login) })
  7. 在页面中创建一个 id 为 app 的 div 元素,作为我们 vm 实例要控制的区域
    8.注意:. Vue-loader在15.*之后的版本都是 vue-loader的使用都是需要伴生 VueLoaderPlugin的。所以,需要在 webpack.config.js 中 引入 const VueLoaderPlugin = require('vue-loader/lib/plugin'); 在 plugin 中加入 new VueLoaderPlugin()

结合webpack使用vue-router

  1. 在项目入口文件main.js中

导入 vue-router 包

import VueRouter from 'vue-router'

手动安装 VueRouter

Vue.use(VueRouter)

  1. 导入自定义的路由模块

import router from './router.js'

  1. 将路由对象挂载到 vm 实例上

     var vm = new Vue({
         el:'#app',
         render: c => c(app),// render 会把 el 指定的容器中所有内容都清空覆盖,所以不能把路由的 router-view 和 router-link 写在里面
         // 4. 将路由对象挂载到 vm 上
         router
    
     })
    
  2. 自定义路由模块文件router.js示例

     import VueRouter from 'vue-router'
     // 导入 Account 组件
     import account from './main/Account.vue'
     // 导入 GoodsList 组件
     import goodslist from './main/GoodsList.vue'
     //导入Account的两个子组件
     import login from './subcom/login.vue'
     import register from './subcom/register.vue'
    
     //3. 创建路由对象
     var router = new VueRouter({
         routes: [
             // acount goodslist
             { path: '/account',component: account,
             children: [
                 { path: 'login', component: login },
                 { path: 'register', component: register }
             ]
             },
             { path: '/goodslist',component: goodslist },
         ]
     })
    
     // 把路由对象暴露出去
     export default router
    
  3. 在对应的 vue 文件中添加容器示例

     <router-link to="/account/login">登录</router-link>
     <router-link to="/account/register">注册</router-link>
    
     <router-view></router-view>
    
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容