近来在做web开发,以vue作为web前端,从0开始一些简单的学习。

下面是一些基本的vue概念和一些学习代码,在学习时逐步记录下来。


VUE

是一个javascript的渐进式的用户界面网页开发框架,便于我们快速开发网站。

竞争对手

Angular - 学习曲线比较陡峭

ReactJS - 代码可读性比较差

EmberJS,Knockout等

一般来说,JQuery算第一代js网页开发技术,Angular算第二代,React,Vue算第三代。

推荐学习站点

https://cn.vuejs.org/

http://vuejs-templates.github.io/

最基本支持

VUE加强套餐

vue-chartjs

vue-fa

vee-validate

eslint-plugin-vue

vue-lazyload

axios

vuedraggable

vue-socket.io

vue-multiselect

vuejs-datepicker

vue-element-admin

vue-typer

vue-good-table

vue ui/ux 框架

nuxtjs

  • 基于vue的通用应用框架

    • 支持自动代码分层
    • 服务器渲染
    • 异步数据
    • 静态文件服务
    • 压缩打包js和css
    • 本地开发热加载
    • 集成eslint
    • 支持http/2推送
    • 支持多种样式处理:sass, less, stylus等
  • https://nuxtjs.org/

学习代码

1. HelloWorld

  • 添加引用
  <script src="https://unpkg.com/vue/dist/vue.js"></srcipt>
  • 添加html代码块
  <div id="myApp">
    {{ message }}
  </div>
  • 添加javascript代码块
  var myApp = new Vue({
    el: "#myApp",   // 对应div id
    data: {
        message: "Hello, world!"    // 此处定义变量,HTML中使用
    }
  });
  • 完整代码
  <!DOCTYPE html>
  <html>
      <head>
          <title>freeknight Vue test</title>
          <script src="https://unpkg.com/vue/dist/vue.js"></script>
      </head>
      <body>
          <div id='myApp'>
              {{ message }}
          </div>
          <script>
              var myApp = new Vue({
                  el: '#myApp',
                  data: {
                      message: "Hello, world!"
                  }
              });
          </script>
      </body>
  </html>

**流程控制符 **

v-if, v-for

<!DOCTYPE html>
<html>
    <head>
        <title>freeknight Vue test 2</title>
        <script src="https://unpkg.com/vue/dist/vue.js"></script>
    </head>
    <body>
        <div id='myApp'>
            <h3>游戏列表</h3>
            <div v-if="isCanBeSeen">可控制隐藏项</div>
            <ol>
                <li v-for="game in games">{{game.title}} / {{game.price}}元</li>
            </ol>
        </div>
        <script>
            var myApp = new Vue({
                el: '#myApp',
                data: {
                    isCanBeSeen: true,  // 控制中间的div项是否存在。注意,不仅仅是不显示,而是直接不存在,不会加入dom树。
                    games:[
                        {title:"游戏名1", price:200},
                        {title:"游戏名2", price:300},
                        {title:"游戏名3", price:500},
                    ],
                }
            });
        </script>
    </body>
</html>

**用户输入绑定 **

v-model 将用户输入绑定到一个js变量,以供其他地方访问使用。

<!DOCTYPE html>
<html>
    <head>
        <title>freeknight Vue test 3</title>
        <script src="https://unpkg.com/vue/dist/vue.js"></script>
    </head>
    <body>
        <div id='myApp'>
            <input v-model='myGame'></input>
            <p>你最喜欢的游戏是: {{myGame}}</p>
        </div>
        <script>
            var myApp = new Vue({
                el: '#myApp',
                data: {
                    myGame: "默认游戏名",    // 这个变量被v-model绑定。当<input>更变,myGame变量更变,也就导致 <p> 元素数据发生更变。
                }
            });
        </script>
    </body>
</html>

按钮事件响应

v-on:{ click,keydown,keyup,dbclick,load …} 就是绑定按钮各种状态的事件处理

<!DOCTYPE html>
<html>
    <head>
        <title>freeknight Vue test 4</title>
        <script src="https://unpkg.com/vue/dist/vue.js"></script>
    </head>
    <body>
        <div id='myApp'>
            <p>你最喜欢的游戏是: {{myGame}}</p>
            <button v-on:click="btnClick('游戏1')">游戏1</button>
            <button v-on:click="btnClick('游戏2')">游戏2</button>
            <!-- 下面的 @ 符号,等同于 v-on: 的简写 -->
            <button @click="btnClick('游戏3')">游戏3</button>
        </div>
        <script>
            var myApp = new Vue({
                el: '#myApp',
                data: {
                    myGame: "默认游戏名"
                }, 
                methods:{
                    btnClick: function(gameName){
                        this.myGame = gameName;
                    }
                }
            });
        </script>
    </body>
</html>

组件

component 一个功能的区域块,有一个自定义的html tag标签。

v-bind 进行组件属性绑定

<!DOCTYPE html>
<html>
    <head>
        <title>freeknight Vue test 5</title>
        <script src="https://unpkg.com/vue/dist/vue.js"></script>
    </head>
    <body>
        <div id='myApp'>
            <ol>
                <!-- v-bind 进行参数/属性绑定 -->
                <game-item v-for="game in games" v-bind:eachgame="game"></game-item>
            </ol>
        </div>
        <script>
            // 自定义组件 game-item
            Vue.component('game-item',{
                props: ['eachgame'],    // 组件的一个属性
                template: '<li>{{ eachgame.title }}</li>'       // 组件的渲染模板
            });

            var myApp = new Vue({
                el: '#myApp',
                data: {
                    games:[
                        {title:"游戏名1", price:200},
                        {title:"游戏名2", price:300},
                        {title:"游戏名3", price:500},
                    ]
                }
            });
        </script>
    </body>
</html>

过滤器属性

filter,做一些变量数据格式化的操作。例如数值的简单再计算,日期的格式调整,字母大小写之类

<!DOCTYPE html>
<html>
    <head>
        <title>freeknight Vue test 6</title>
        <script src="https://unpkg.com/vue/dist/vue.js"></script>
    </head>
    <body>
        <div id='myApp'>
            <p>{{ message }}</p>
            <p>{{ message | toupper }}</p>
            <p>{{ floatValue }}</p>
            <p>{{ floatValue | topercent }}</p>
        </div>
        <script>
            var myApp = new Vue({
                el: '#myApp',
                data: {
                    message: "Hello, world!",
                    floatValue: 0.3,
                },
                filters:{
                    toupper: function(value){
                        return value.toUpperCase(); // 最终显示  HELLO, WORLD!
                    },
                    topercent: function(value){
                        return value * 100 + "%";   // 最终显示  30%
                    }
                }
            });
        </script>
    </body>
</html>

计算属性

computed,对数据进行计算加工后得到新的数据在进行显示,和filter是类似的

<!DOCTYPE html>
<html>
    <head>
        <title>freeknight Vue test 7</title>
        <script src="https://unpkg.com/vue/dist/vue.js"></script>
    </head>
    <body>
        <div id='myApp'>
            <p>{{ floatValue }}元</p>
            <p>{{ USDlloar }}美元</p>
        </div>
        <script>
            var myApp = new Vue({
                el: '#myApp',
                data: {
                    floatValue: 513,
                },
                computed:{
                    USDlloar: function(){
                        return Math.round(this.floatValue / 7.1);
                    },
                }
            });
        </script>
    </body>
</html>

观察一个属性

watch 监视一个变量,一旦变化,则做出事件触发。

<!DOCTYPE html>
<html>
    <head>
        <title>freeknight Vue test 8</title>
        <script src="https://unpkg.com/vue/dist/vue.js"></script>
    </head>
    <body>
        <div id='myApp'>
            <p>{{ floatValue }}元</p>
            <p>{{ USDlloar }}美元</p>
            <button @click="btnClick(100)">加100块</button>
        </div>
        <script>
            var myApp = new Vue({
                el: '#myApp',
                data: {
                    floatValue: 513,
                    USDlloar: 0,
                },
                watch: {    
                    // 当 floatValue 的值发生变化,会执行该变量的watch函数
                    floatValue: function(newVal, oldVal){
                        console.log(oldVal, '->', newVal);
                        this.USDlloar = Math.round(this.floatValue / 7.1);
                    }
                },
                methods:{
                    btnClick: function(newVal){
                        this.floatValue += newVal;
                    }
                }
            });

            // 进行初始化,这个赋值也会激活watch
            myApp.floatValue = 515;
        </script>
    </body>
</html>

设置计算属性

setter 可以对计算属性进行修改时获得事件触发。

<!DOCTYPE html>
<html>
    <head>
        <title>freeknight Vue test 9</title>
        <script src="https://unpkg.com/vue/dist/vue.js"></script>
    </head>
    <body>
        <div id='myApp'>
            <p>{{ floatValue }}元</p>
            <p>{{ USDlloar }}美元</p>
            <p>{{ USDlloarSetter }}美元</p>
            <button @click="btnClick(100)">加价100元</button>
            <button @click="btnClickUSD(100)">加价100刀</button>
        </div>
        <script>
            var myApp = new Vue({
                el: '#myApp',
                data: {
                    floatValue: 513,
                },
                computed:{
                    // 这种函数方式,只适合单项计算,不适合反向影响
                    USDlloar: function(){
                        return Math.round(this.floatValue / 7.1);
                    },

                    // 这个对象方式,可以双向影响。在 get, set 时均会触发事件
                    USDlloarSetter: {
                        get: function(){
                            return Math.round(this.floatValue / 7.1);
                        },
                        set: function(value){
                            this.floatValue = Math.round(value * 7.1);
                        }
                    },
                },
                methods:{
                    btnClick: function(newVal){
                        this.floatValue += newVal;
                    },
                    btnClickUSD: function(newVal){
                        this.USDlloarSetter += newVal;
                    },
                }
            });
        </script>
    </body>
</html>

属性绑定

v-bind为一个标签的一个属性绑定变量。

<!DOCTYPE html>
<html>
    <head>
        <title>freeknight Vue test 10</title>
        <script src="https://unpkg.com/vue/dist/vue.js"></script>
        <link href="myCss.css" rel="stylesheet">
    </head>
    <body>
        <div id='myApp'>
            <!-- 在 :冒号后面是要绑定的属性,这里我们绑定的是 class 类-->
            <!-- 当isActive变量为true时,本 div 的class则为 active, 受到css影响,字体为红色 -->
            <!-- 当isActive变量为flase时,本 div 的class则为 空, 不受到css影响,字体为默认黑色 -->
            <div v-bind:class="{active:isActive}"> 红色文本1 </div>
            <!-- 可以简写为 :attribute -->
            <div :class="{active:isActive}"> 红色文本2 </div>
            <div>
                <!-- 这里我们绑定的是 提示信息 属性-->
                <a href="#" :title="hintText">鼠标移过来看看</a>
            </div>
            <button @click="btnClick">更变状态</button>
        </div>
        <script>
            var myApp = new Vue({
                el: '#myApp',
                data: {
                    isActive: true,
                    hintText: "这是提示信息",
                },
                methods:{
                    btnClick: function(){
                        this.isActive = (!this.isActive);
                        this.hintText += "@";
                    }
                }
            });
        </script>
    </body>
</html>
body{
    font-size: 24px;
}

.active{
    color: red;
}

类对象绑定

和上面差不多,只是不再绑定单一属性,而是绑定一系列属性的对象

<!DOCTYPE html>
<html>
    <head>
        <title>freeknight Vue test 11</title>
        <script src="https://unpkg.com/vue/dist/vue.js"></script>
        <link href="myCss.css" rel="stylesheet">
    </head>
    <body>
        <div id='myApp'>
            <!-- 在 :冒号后面是要绑定的属性,这里我们绑定的是 class 类-->
            <!-- 当isActive变量为true时,本 div 的class则为 active, 受到css影响,字体为红色 -->
            <!-- 当isActive变量为flase时,本 div 的class则为 空, 不受到css影响,字体为默认黑色 -->
            <div v-bind:class="myclass"> 红色文本1 </div>
            <!-- 可以简写为 :attribute -->
            <div :class="myclass"> 红色文本2 </div>

            <button @click="btnClick">更变状态</button>
        </div>
        <script>
            var myApp = new Vue({
                el: '#myApp',
                data: {
                    myclass:{
                        active: true,
                        bigfont: true,
                    }
                },
                methods:{
                    btnClick: function(){
                        this.myclass.active = (!this.myclass.active);
                        this.myclass.bigfont = (!this.myclass.bigfont);
                    }
                }
            });
        </script>
    </body>
</html>
body{
    font-size: 24px;
}

.active{
    color: red;
}

.bigfont{
    font-weight: bolder;
    font-size: 32px;
}

条件渲染

v-if, v-else-if, v-else

<!DOCTYPE html>
<html>
    <head>
        <title>freeknight Vue test 12</title>
        <script src="https://unpkg.com/vue/dist/vue.js"></script>
    </head>
    <body>
        <div id='myApp'>
            <h1 v-if="result == 0">无成绩</h1>
            <h1 v-else-if="result >= 80">考的很好</h1>
            <h1 v-else-if="result < 60">没及格</h1>
            <h1 v-else>考的一般</h1>
            <button @click="btnClick">考试成绩</button>
        </div>
        <script>
            var myApp = new Vue({
                el: '#myApp',
                data: {
                    result: 0,
                },
                methods:{
                    btnClick: function(){
                        this.result = Math.round(Math.random() * 100);
                    }
                }
            });
        </script>
    </body>
</html>

元素显示

v-show 标记是否显示,注意,不显示的依然会在dom树中。和上面的 v-if不一样

<!DOCTYPE html>
<html>
    <head>
        <title>freeknight Vue test 13</title>
        <script src="https://unpkg.com/vue/dist/vue.js"></script>
    </head>
    <body>
        <div id='myApp'>
            <h1 v-show="result">这个 h1 标签永久存在。</h1>
            <button @click="btnClick">修改可见</button>
        </div>
        <script>
            var myApp = new Vue({
                el: '#myApp',
                data: {
                    result: true,
                },
                methods:{
                    btnClick: function(){
                        this.result = !this.result;
                    }
                }
            });
        </script>
    </body>
</html>

对象迭代

v-for 和前面的一个v-for区别是,之前处理的是数组,这次处理的是一个对象。这个方法很适合进行debug。

<!DOCTYPE html>
<html>
    <head>
        <title>freeknight Vue test 14</title>
        <script src="https://unpkg.com/vue/dist/vue.js"></script>
    </head>
    <body>
        <div id='myApp'>
            <h3>js对象迭代</h3>
            <div v-for="(value, key) in games">{{key}}:{{value}}</div>
        </div>
        <script>
            var myApp = new Vue({
                el: '#myApp',
                data: {
                    isCanBeSeen: false,
                    games:{
                        title: "名字",
                        price: 200,
                    }
                }
            });
        </script>
    </body>
</html>

事件处理

v-on:{event} 简写 @{event} 当用户和页面交互时,获取用户事件信息。

<!DOCTYPE html>
<html>
    <head>
        <title>freeknight Vue test 15</title>
        <script src="https://unpkg.com/vue/dist/vue.js"></script>
    </head>
    <body>
        <div id='myApp'>
            <input id="myInput" v-on:keyup="myInputKeyup($event)">
            <button id="myButton" @click="btnClick($event)">ok</button>
        </div>
        <script>
            var myApp = new Vue({
                el: '#myApp',
                data: {
                },
                methods:{
                        myInputKeyup: function(event){
                            this.debugLog(event);
                        },
                        btnClick: function(event){
                            this.debugLog(event);
                        },
                        debugLog: function(event){
                            console.log("组件类型:", event.srcElement.tagName,
                            "组件ID:",     event.srcElement.id,
                            "组件数据:",   event.srcElement.innerHTML,
                            "用户按键:",    event.key? event.key: "无")
                        }
                    }
            });
        </script>
    </body>
</html>

表单控件绑定

这个非常重要

v-model 给组件绑定一个数据,注意,这里是双向绑定

<!DOCTYPE html>
<html>
    <head>
        <title>freeknight Vue test 16</title>
        <script src="https://unpkg.com/vue/dist/vue.js"></script>
    </head>
    <body>
        <div id='myApp'>
            <!-- 注意:这里都是双向绑定。即,若修改了input中的数据,message变量值也发生了改变 -->
            
            <!-- 输入框 -->
            <input type="text" v-model="message" placeholder="没东西">
            <p>Message 变量值是 {{message}}</p>
            <div></div>
            <!-- 多选框 -->
            <input type="checkbox" id="cb1" value="第一项的值" v-model="checkboxValue">
            <label for="cb1">第一项</label>
            <input type="checkbox" id="cb2" value="第二项的值" v-model="checkboxValue">
            <label for="cb2">第二项</label>
            <input type="checkbox" id="cb3" value="第三项的值" v-model="checkboxValue">
            <label for="cb3">第三项</label>
            <p>checkboxValue 变量值是 {{checkboxValue}}</p>
            <div></div>
            <!-- 单选框-->
            <input type="radio" id="r1" value="第1项的值" v-model="singleCheckboxValue">
            <label for="r1">第1项</label>
            <input type="radio" id="r2" value="第2项的值" v-model="singleCheckboxValue">
            <label for="r2">第2项</label>
            <input type="radio" id="r3" value="第3项的值" v-model="singleCheckboxValue">
            <label for="r3">第3项</label>
            <p>singleCheckboxValue 变量值是 {{singleCheckboxValue}}</p>
            <div></div>
            <!-- 单选下拉框 -->
            <select v-model="selectValue">
                <option v-for="question in questionsValues">{{question}}</option>
            </select>
            <p>selectValue 变量值是 {{selectValue}}</p>
            <div></div>
            <!-- 复选下拉框 -->
            <select v-model="selectValues" multiple style="height: 70px;">
                <option v-for="question in questionsValues">{{question}}</option>
            </select>
            <p>selectValues 变量值是 {{selectValues}}</p>
        </div>
        <script>
            var myApp = new Vue({
                el: '#myApp',
                data: {
                    message: "Hello, world!",
                    checkboxValue: [],
                    singleCheckboxValue: "",
                    selectValue: "",
                    selectValues: [],
                    questionsValues :[
                        "问题1",
                        "问题2",
                        "问题3",
                        "问题4",
                        "问题5",
                    ]
                }
            });
        </script>
    </body>
</html>

表单控件修饰符

.lazy

此时用户输入不再做绑定数据params的实时更新处理,仅在控件的onChange事件中更新该变量。目的是提高页面性能。

.number

将用户输入内容转换为数值类型,若输入为非数值,则返回NaN

.trim

自动去除用户输入内容两端的空格

<!DOCTYPE html>
<html>
    <head>
        <title>freeknight Vue test 17</title>
        <script src="https://unpkg.com/vue/dist/vue.js"></script>
    </head>
    <body>
        <div id='myApp'>
            <h1>用户注册</h1>
            <div>
                <label for="username">用户:</label>
                <input type="text" id="username" v-model.lazy="username" @change="checkUsername">
                <span v-if="isUsernameOK">可以注册</span>
            </div>
            <div>
                <label for="age">年龄:</label>
                <input type="number" id="age" v-model.number="age">
            </div>
            <div>
                <label for="content">个人看法: </label>
                <textarea id="content" v-model.trim="content" cols="55" rows="8"></textarea>
            </div>

            <h4>信息提示</h4>
            <p>{{username}}</p>
            <p>{{age}}</p>
            <p>{{content}}</p>
        </div>
        <script>
            var myApp = new Vue({
                el: '#myApp',
                data: {
                    username: "",
                    isUsernameOK: false,
                    age: "",
                    content: ""
                },
                methods:{
                    checkUsername: function(){

                        if(this.username.length <= 0){
                            this.isUsernameOK = false;
                        }else{
                            this.isUsernameOK = true;
                        }
                    }
                }
            });
        </script>
    </body>
</html>

组件作用域

Component 一个区域内容,类似自定义一个标签。

Portlet 某一个页面功能

<!DOCTYPE html>
<html>
    <head>
        <title>freeknight Vue test 18</title>
        <script src="https://unpkg.com/vue/dist/vue.js"></script>
    </head>
    <body>
        <div id='myApp'>
            <today-weather></today-weather>

            <another-weather></another-weather>
        </div>
        <script>
            // 注册一个全局的组件,不属于 myApp,而是属于VUE全局,注意:全局组件必须声明在 new Vue() 之前。
            Vue.component('today-weather',{
                template: '<div>天气预报模板</div>'
            });
            // 可复用的局部组件,声明为一个普通的js对象
            var anotherWeatherComponent = {
                template: '<div>另外一个可复用的天气预报模板</div>'
            };
            var myApp = new Vue({
                el: '#myApp',
                components:{
                    // 绑定HTML标签和组件模板对象,这个component仅仅可被本 myApp 内使用。
                    'another-weather': anotherWeatherComponent
                },
            });
        </script>
    </body>
</html>v

特殊的组件

因为HTML的限制,部分元素只能在浏览器解析HTML之后才会去获取组件模板内容,典型的例如表格,排序组件 ol ul table select option 等元素。

如果写成

<table>
    <my-compenent></my-compenent>
</table>

是不能显示正确位置的。需要使用特殊的 is 属性如下:

<table>
    <tr is='my-compenent'></tr>
</table>

组件中的数据

组件中的变量数据,不能使用data属性,只能使用data函数。

<!DOCTYPE html>
<html>
    <head>
        <title>freeknight Vue test 19</title>
        <script src="https://unpkg.com/vue/dist/vue.js"></script>
    </head>
    <body>
        <div id='myApp'>
            <div>
                今天的天气是:<today-weather></today-weather>
                明天的天气是:<tomorrow-weather></tomorrow-weather>
            </div>
        </div>
        <script>
            Vue.component("today-weather",{
                template: "<strong>{{todayWeatherValue}}</strong>",
                data:{
                    todayWeatherValue: '大雪',  // 这是一个数据data属性,将会js报错。
                }
            });
            Vue.component("tomorrow-weather",{
                template: "<strong>{{tomorrowWeatherValue}}</strong>",
                data: function(){
                    return{
                        tomorrowWeatherValue: '晴天',  // 这是一个data函数,这样则是正确的
                    };
                }
            });
            var myApp = new Vue({
                el: '#myApp',
            });
        </script>
    </body>
</html>

组件间的数据传递

props 接收外界数据或变量

<!DOCTYPE html>
<html>
    <head>
        <title>freeknight Vue test 20</title>
        <script src="https://unpkg.com/vue/dist/vue.js"></script>
    </head>
    <body>
        <div id='myApp'>
            <text-result :score="50"></text-result>
            <text-result :score="70"></text-result>
            <text-result :score="100"></text-result>
            <hr/>
            <div>请输入名字: <input v-model="yourname"></div>
            <say-hello :pname="yourname">
        </div>
        <script>
            Vue.component('text-result', {
                props: ['score'],   // 这里是接数据
                template: '<div><strong>{{score}}分 {{result}}</strong></div>',
                computed:{          // 顺道复习conputed
                    result: function(){
                        var strResult = "未知";
                        if(this.score < 60){
                            strResult = "不及格";
                        }else if(this.score >= 80){
                            strResult = "成绩很好";
                        }else{
                            strResult = "马马虎虎";
                        }
                        return strResult;
                    }
                }
            });
            Vue.component('say-hello',{
                props: ['pname'],   // 这里接的是变量
                template: '<div><strong> hello, {{pname}} </strong></div>'
            });
            var myApp = new Vue({
                el: '#myApp',
                data:{
                    yourname: "freeknight"
                }
            });
        </script>
    </body>
</html>

参数检查验证

type, validator, default

<!DOCTYPE html>
<html>
    <head>
        <title>freeknight Vue test 1</title>
        <script src="https://unpkg.com/vue/dist/vue.js"></script>
    </head>
    <body>
        <div id='myApp'>
            <show-member-info name="freeknight" :age="18" :detail="{address:'singaporo', language:'日语'}">
            </show-member-info>
        </div>
        <script>
            Vue.component('show-member-info', {
                props:{
                    name: {
                        type: String,
                        required: true,                 // 该组件的该参数项为强制存在
                    },
                    age:{
                        type: Number,
                        validator: function(value){     // 有效性校验
                            return value >= 0 && value <= 100;
                        }
                    },
                    detail:{
                        type: Object,
                        default: function(){            // 默认值
                            return {
                                address: 'NoPlace',
                                language: '英语'
                            }
                        }
                    }
                },
                template: '<div>名字: {{this.name}} <br/>'
                        + '年龄: {{this.age}} <br/>'
                        + '地址: {{this.detail.address}} <br/>'
                        + '语言: {{this.detail.language}} </div>'
            });
            var myApp = new Vue({
                el: '#myApp',
            });
        </script>
    </body>
</html>

事件的传递

v-on 监听消息,$emit 提交事件

<!DOCTYPE html>
<html>
    <head>
        <title>freeknight Vue test 22</title>
        <script src="https://unpkg.com/vue/dist/vue.js"></script>
    </head>
    <body>
        <div id='myApp'>
            <add-method :a="6" :b="12" v-on:add_event="getAddResult"></add-method>
            <hr/>
            <h3>{{result}}</h3>
        </div>
        <script>
            Vue.component('add-method',{
                props: ['a', 'b'],
                // 按钮,点击事件为add
                template: '<div><button v-on:click="add">加</button></div>',
                methods:{
                    add: function(){
                        var value = 0;
                        value = this.a + this.b;
                        console.log(value);
                        // 提交一个add_event事件
                        this.$emit( 'add_event', { 
                            result: value 
                        });
                    }
                }
            });
            var myApp = new Vue({
                el: '#myApp',
                data: {
                    result: 0
                },
                methods:{
                    getAddResult: function(val){
                        this.result = val.result;
                    }
                }
            });
        </script>
    </body>
</html>

slot插槽

slot 是父附件和子附件性通讯的方式。

<!DOCTYPE html>
<html>
    <head>
        <title>freeknight Vue test 23</title>
        <script src="https://unpkg.com/vue/dist/vue.js"></script>
    </head>
    <body>
        <div id='myApp'>
            <say-to pname="小A">你吃了没</say-to>
            <say-to pname="小B">你饿了没</say-to>
            <say-to pname="小C">你给他们做饭吧</say-to>

            <nba-all-stars c="奥尼尔" pf="加内特">
                <span slot="sf">皮克</span>
                <span slot="sg">丘吉尔</span>
                <span slot="pg">特朗普</span>
            </nba-all-stars>
        </div>
        <script>
            Vue.component('say-to',{
                props: ['pname'],
                template: '<div> 你好, {{pname}}! '
                    + '<slot></slot>'       // 这里,将上面标签中的内容"你吃了没""你饿了没"等,嵌入到组件内容中了
                    + '</div>',
            });
            Vue.component('nba-all-stars',{
                props: ['c', 'pf'],         // 这是普通组件属性
                template: '<div>'
                    +'<p>中锋: {{c}}</p>'
                    +'<p>大前: {{pf}}</p>'
                    +'<p>小前: <slot name="sf"></slot></p>' // 带标签名的slot
                    +'<p>分卫: <slot name="sg"></slot></p>'
                    +'<p>控卫: <slot name="pg"></slot></p>'
                    +'</div>',
            });
            var myApp = new Vue({
                el: '#myApp',
            });
        </script>
    </body>
</html>

Vue cli安装

c:\Users\freeknight>npm -v              // 查看npm版本
6.14.4

c:\Users\freeknight>npm show vue-cli    // 查看npm远程仓库中的vue-cli版本
报错…… npm ERR! code ECONNRESET request to https://registry.npmjs.org/vue-cli failed..

c:\Users\freeknight>npm config set registry http://registry.npmjs.org/      // 将npm请求从https改为http

c:\Users\freeknight>npm show vue-cli    // 这次OK了
vue-cli@2.9.6 | MIT | deps: 20 | versions: 35
dist-tags:
latest: 2.9.6
published a year ago by yyx990803 <yyx990803@gmail.com>

c:\Users\freeknight>npm install -g vue-cli@2.9.6    // 下载vue
+ vue-cli@2.9.6
added 238 packages from 205 contributors in 62.513s

c:\Users\freeknight>vue -V              // 查看本地vue版本
2.9.6
c:\Users\freeknight>vue -h              // 查看帮助
Usage: vue <command> [options]
Options:
  -V, --version  output the version number
  -h, --help     output usage information
Commands:
  init           generate a new project from a template // 通过模板去初始化一个工程
  list           list available official templates      // 查看可用的官方模板列表,官方模板到 https://github.com/vuejs-templates 这里看就好
  build          prototype a new project                // 发包发布自己的项目
  create         (for v3 warning only)
  help [cmd]     display help for [cmd]

c:\Users\freeknight>vue webpack myProjectName       // 使用webpack默认模板去创建一个叫myProject的项目
c:\Users\freeknight>vue username/repo myMyProjectName // 使用 GitHub 仓库做模板创建

webpack模板工程

其详细文档可参见: http://vuejs-templates.github.io/webpack/prerender.html

c:\Users\freeknight>vue init webpack myweb  // 创建webpack的模板工程

? Project name myweb                        // 工程名确认
? Project description A Vue.js project      // 描述文字确认
? Author d******8@gmail.com                 // 开发者邮箱确认
? Vue build
> Runtime + Compiler: recommended for most users    // 注意:建议选这个带compiler编译器的。
? Vue build standalone
? Install vue-router? Yes
? Use ESLint to lint your code? Yes
? Pick an ESLint preset Standard
? Set up unit tests No
? Setup e2e tests with Nightwatch? No
? Should we run `npm install` for you after the project has been created? (recommended) npm

   vue-cli · Generated "myweb".
   
   // One thousand years later......
   // One thousand years later......
   // One thousand years later......
   
# Project initialization finished!
# ========================

To get started:

  cd myweb
  npm run dev
  
c:\Users\freeknight>cd myweb        // 进入项目文件夹
c:\Users\freeknight>npm run dev     // 其实是在执行 package.json 中的scripts对象中的 dev 脚本。
 DONE  Compiled successfully in 21276ms 22:04:36
 I  Your application is running here: http://localhost:8080
 可以本地浏览器打开 http://localhost:8080 查看
 
c:\Users\freeknight>npm run build   // 打包发布输出,其实是在执行 package.json 中的scripts对象中的 build 脚本。
会将发布版本生成到dist目录下。使用该目录进行发布即可

该项目工程中最重要的结构依然是

  • src 源文件文件夹

  • dist 打包发布文件夹

  • package.json 项目工程设置文件

VUE项目中导入bootstrap4

引入bootstrap目的是让网页更加漂亮。

$cd myweb
$npm install bootstrap --save --save-exact  // 安装bootstrap,--save会将其直接加入到package.json项目文件中 --save-exact表示其版本号前不再有 "dependencies": {
    "bootstrap": “^4.4.1”} 的这个 ^ 符号,表示版本指定,不允许智能调整。
+ bootstrap@4.4.1
added 1 package from 2 contributors and audited 12619 packages in 66.6s

安装完毕bootstrap后,找到myweb目录下的src目录中的main.js,这个是文件入口。 在这里导入bootstrap,添加以下一行

import 'bootstrap/dist/css/bootstrap.min.css'

然后就可以使用bootstrap了。 如果我们想测试是否导入bootstrap成功,可以修改 src 目录中的 app.vue 入口模板文件做个测试

<template>
  <div id="app">
    <img src="./assets/logo.png">
    <hr/>
    <button class="btn btn-success">测试bootstrap</button>    // 这里加入bootstrap的CSS样式
    <button class="btn btn-danger">测试bootstrap</button>     // 这里加入bootstrap的CSS样式
    <hr/>
    <router-view/>
  </div>
</template>

执行npm start,你本地浏览器看看按钮的效果就知道了。(或者移除上面的import,可以进行对比)

当然,这是传统的方式使用bootstrap+VUE,更好的方式是使用 https://bootstrap-vue.org/它提供了一系列的bootstrap的组件tag,例如更美观的按钮

<b-button variant="success">新样式button</b-button>

VUE项目中引入Ajax库 Axios

引入ajax的目的是,从服务器取得数据,使网页有远程存取能力。

axios不仅支持浏览器,也可用于node.js开发。

$cd myweb
$npm install --save --save-exact axios vue-axios // 这里安装了两个库,一个axios基本库,一个vue-axios封装的vue组件,可便于我们在vue中使用axios
+ axios@0.19.2
+ vue-axios@2.1.5
added 4 packages from 8 contributors, updated 1 package and audited 12624 packages in 47.866s

安装完毕后,一样找到src目录下的main.js,进行注册。

import axios from 'axios'
import VueAxios from 'vue-axios'

// 注意,它还需要指定vue使用
Vue.use(VueAxios, axios)

然后我们测试是否成功,可以找到src目录下的components中的 helloworld.vue,直接复制下面内容覆盖helloworld.vue即可。

<!-- HTML部分,我给基本清理了 -->
<template>
  <div class="hello">
    <pre>{{content}}</pre>  <!-- 显示的组件 -->
  </div>
</template>

<!-- JS部分 -->
<script>
export default { /* 这里表示导出helloworld这个组件 */
  name: 'HelloWorld',
  data () {
    return {
      content: '没有内容'
    }
  },
  // 该页面加载完毕后,自动执行的函数 mounted
  mounted () {
    var myApiAddress = 'https://newsapi.org/v2/top-headlines'
    this.axios.post(myApiAddress).then(
      body => {
        this.content = body.data /* 将从API请求的数据返回,设置到content中并显示出来 */
      }
    )
  }
}
</script>

<!-- CSS样式部分,内容我给删除了 -->
<!-- 添加 "scoped" 属性表示这个css样式仅仅作用于当前组件,不会影响其他组件 -->
<style scoped>

</style>

VUE自定义样式单

  • 进入assets目录,创建mycss.css文件
.myClass1 {
    color: red;
    border: 1px solid blue;
}
  • 打开app.Vue
<script>
import './assets/mycss.css'   // 添加自定义css文件
export default {
  name: 'App',
  data(){
    return "";
  }
}
</script>
  • 添加测试
<template>
  <div id="app">
    <img src="./assets/logo.png">
    <div class='myClass1'>你好</div>
    <router-view/>
  </div>
</template>

webpack项目工程结构

.
├── build/                      # webpack配置文件,通常不应该修改。如果你想自定义webpack loader,可以看webpack.base.conf.js
│   └── ...
├── config/                     # 工程设置文件 【发布测试运维时需要处理这里】
│   ├── index.js                # 核心项目设置文件
│   └── ...
├── dist/                       # 最终生成发布目录,执行 npm run build得到
├── src/                        # 最主要代码工作文件夹
│   ├── main.js                 # app入口函数
│   ├── App.vue                 # app主组件
│   ├── components/             # 组件文件夹 【95%时间在这里】
│   │   └── ...                 # 最核心开发文件夹。一个.vue文件对应一个组件,一个组件包括 template(HTML内容),script(组件js脚本),style(组件css样式)
│   └── assets/                 # 组件资源目录,例如图片,全局css等
│       └── ...
├── static/                     # 静态资源,这里面的资源在最后发布时会直接被拷贝到发布文件夹。一般是视频啦,提供用户下载的可执行文件之类
├── test/                       # 测试相关目录文件夹
│   └── unit/                   # unit 单元测试文件夹
│   │   ├── ...
│   └── e2e/                    # e2e 测试文件夹
│   │   ├── ...
├── .babelrc                    # babel 配置文件
├── .editorconfig               # 开发ide的配置文件
├── .eslintrc.js                # eslint 配置
├── .eslintignore               # eslint 规则例外文件
├── .gitignore                  # git 管理例外文件
├── .postcssrc.js               # postcss 配置文件
├── index.html                  # index.html的一个单页面模板,整个程序的真正入口。它调src/main.js,然后main.js加载了最基本的一个组件叫App.vue,并且使用src/router加载大量子页面作为二级菜单三级菜单,每个子菜单页面对应各个不同的component以及子component。
├── package.json                # npm包依赖管理和构建密码命令   【前期添加依赖时要关注这里】
└── README.md                   

VUE路由router

https://router.vuejs.org/ 可以看中文文档 https://router.vuejs.org/zh/

它是用来做网页页面路由的。

安装 很简单,但一般情况下都是安装好的,并不需要手动处理。

$npm install vue-router --save --save-exact

使用 的时候需要查看 src/main.js

import router from './router'

// 这里是入口组件,在这里使用router对象,则意味着所有子组件都可以使用到router对象
new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})

以及 src/router/index.js

import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'    // 添加我们的组件
import HelloWorld2 from '@/components/HelloWorld2'

Vue.use(Router)

// 输出url映射类 Router
export default new Router({
  routes: [
    {
      path: '/',        // 完成一个url和一个组件之间的映射
      name: 'HelloWorld',
      component: HelloWorld
    },
    {
      path: '/Helloworld2',
      name: 'HelloWorld2',
      component: HelloWorld2
    }
  ]
})

如果说 主页面组件中 需要有HelloWorld2这个子页面组件的跳转链接,那么可以修改 主页面.Vue 如下。

<template>
    <div id=app>
        <p>
            <!-- 这就类似于 a 标签和 ref 属性 -->
            <router-link to='/'>HOME</router-link>
            <router-link to='/Helloworld2'>访问helloworld</router-link>
        </p>

        <router-view/>
    </div>
</template>

动态路由

就是路由URL会发生一些变化,例如,http://xxoo.com/player/1 http://xxoo.com/player/2 打开两个不同的页面,但 1,2 这些动态URL内路径不能枚举写死。

创建components/Player.vue 组件

<template>
  <div>
    <h1>球员页面</h1>
    <p>{{detail}}</p>
  </div>
</template>

<script>
export default {
    name: 'Player',
    data() {
        return {
            detail: {}
        }
    },
    mounted() {
        // formLoad初始化时,通过player uid获取球员信息
        this.detail = this.getPlayerDetail(this.$route.params.uid);
    },
    beforeRouteUpdate(to, from, next){
        // route发生更变之前的回调消息,如果没有该函数,则会在切换网页后,detail不变的情况。
        // 因为mounted是init行为,当页面渲染完毕后,不会再次重复被调用
        this.detail = this.getPlayerDetail(to.params.uid);
        next(); // 事件提交给系统处理
    },
    methods:{
        getPlayerDetail(uid){
            // 这里也可以使用 axios 从服务器获取
            switch(uid){
                case '1':
                    return {uid: 1, name: "德玛西亚", point:33 }
                case '2':
                    return {uid: 2, name: "诺克萨斯", point:12 }
                default:
                    return {uid: -1}
                }
            }
        }
    }
</script>

设置路由,修改router/index.js

import Vue from 'vue'
import Router from 'vue-router'
import Player from '@/components/Player'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/player/:uid',
      name: 'Player',
      component: Player
    }
  ]
})

修改App.vue进行测试,传入带不同路径的URL

<template>
  <div id="app">
    <p>
      <router-link to="/">主页</router-link>
      <router-link to="/player/1">德玛西亚</router-link>
      <router-link to="/player/2">诺克萨斯</router-link>
    </p>
    <router-view/>
  </div>
</template>

<script>
import './assets/mycss.css' // 添加自定义css文件
export default {
  name: 'App'
}
</script>

嵌套路由

在动态路由后面添加新的URL,例如:http://xxoo.com/player/1/profile http://xxoo.com/player/2/stats 这样的URL。

创建components/Player/Profile.vue 组件

<template>
    <div>
        <h2>球员介绍: {{$route.params.uid}}</h2>
    </div>
</template>

创建components/Player/Stats.vue 组件

<template>
    <div>
        <h2>球员数据: {{$route.params.uid}}</h2>
    </div>
</template>

修改router/index.js

import Vue from 'vue'
import Router from 'vue-router'
import Player from '@/components/Player'
import PlayerProfile from '@/components/Player/Profile'
import PlayerStats from '@/components/Player/Stats'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/player/:uid',
      name: 'Player',
      component: Player,
      children:[
        {
            path: 'profile',
            component: PlayerProfile,
        },
        {
            path: 'stats',
            component: PlayerStats,
        },
      ]
    }
  ]
})

修改component/player.vue,我们这里还实现了 组件内嵌套子组件 的功能。

<template>
  <div>
    <h1>球员页面</h1>
    <p>{{detail.uid}}</p>
    <p>{{detail.name}}</p>
    <p>{{detail.point}}</p>
    <hr/>
    <!-- 这是在player.vue中嵌入子vue组件 -->
    <!-- 这里绑定的是data中的两个变量profile, stats -->
    <router-link :to="profile">简介</router-link>
    <router-link :to="stats">数据</router-link>
    <hr/>
    <router-view></router-view>
  </div>
</template>

<script>
export default {
    name: 'Player',
    data() {
        return {
            detail: {},
            profile: '',
            stats: '',
        }
    },
    mounted() {
        // formLoad初始化时,通过player uid获取球员信息
        this.detail = this.getPlayerDetail(this.$route.params.uid);
        this.profile = '/player/' + this.$route.params.uid + '/profile';
        this.stats = '/player/' + this.$route.params.uid + '/stats';
    },
    beforeRouteUpdate(to, from, next){
        // route发生更变之前的回调消息,如果没有该函数,则会在切换网页后,detail不变的情况。
        // 因为mounted是init行为,当页面渲染完毕后,不会再次重复被调用
        this.detail = this.getPlayerDetail(to.params.uid);
        this.profile = '/player/' + to.params.uid + '/profile';
        this.stats = '/player/' + to.params.uid + '/stats';
        next(); // 事件提交给系统处理
    },
    methods:{
        getPlayerDetail(uid){
            // 这里也可以使用 axios 从服务器获取
            switch(uid){
                case '1':
                    return {uid: 1, name: "德玛西亚", point:33 }
                case '2':
                    return {uid: 2, name: "诺克萨斯", point:12 }
                default:
                    return {uid: -1}
                }
            }
        }
    }
</script>

路由编程

上面我使用 route-link 标记来生成页面中的标记,然后进行的url转向到其他的组件页面。我们也可以使用新的方式。router.push(location, onComplete?, onAbort?)

下面对App.vue做修改

<template>
  <div id="app">
    
 
    <p>
      <router-link to="/">主页</router-link>
      <router-link to="/player/1">德玛西亚1</router-link>
      <router-link to="/player/2">诺克萨斯1</router-link>
    </p>
    <!-- to前面加冒号,后面则添加json对象,也就等同 router.push(...) -->
    <router-link :to="{name: 'Player', params: {uid: 1}}">德玛西亚2</router-link>
    <router-link :to="{path: '/player/2/stats'}">诺克萨斯2</router-link>
    <hr/>
    <button @click="btnClick(1)">德玛西亚3</button>
    <button @click="btnClick(2)">诺克萨斯3</button>

    <router-view/>
  
  </div>
</template>

<script>
import './assets/mycss.css' // 添加自定义css文件
export default {
  name: 'App',
  methods:{
    btnClick(uid){
      console.log(uid);
      // 按键之后,进行path的重定向
      this.$router.push({path: '/player/${uid}'})
      // 重定向方式2
      // this.$router.push({name: 'Player', params: {uid: uid}})
      // 重定向方式3 ,这样访问的则是 http://xxoo.net/player?uid=1
      // this.$router.push({name: 'Player', query: {uid: uid}})
    }
  }
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

单路由多view

一个页面 (也就是一个路由) 中组合多个组件。

router-view[name] 绑定

components : 注意有s

// index.js
import SettingDetail from '@/components/Setting/Detail'
import SettingHeader from '@/components/Setting/Header'
import SettingSideBar from '@/components/Setting/SideBar'


Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'Home',
      components: {
          myHeader: SettingHeader,
          mySideBar: SettingSideBar,
          myDetail: SettingDetail,
      }
    }
  ]
})

App.vue

<template>
  <div id="app">
    <table>
        <tr>
            <td colspan="2" style="background-color:darkgoldenrod">
                <router-view name="myHeader"></router-view>
            </td>
        </tr>
        <tr>
            <td width="20%" style="background-color:thistle">
                <router-view name="mySideBar"></router-view>
            </td>
            <td width="20%" style="background-color:aquamarine">
                <router-view name="myDetail"></router-view>
            </td>
        </tr>
    </table>
  
  </div>
</template>

URL重定向

alias: URL别名

redirect: 重定向

//index.js
export default new Router({
  routes: [
    {
      path: '/player/:uid',
      name: 'Player',
      // 此时输入 htttp://xxoo.com/about/1 等同于  http://xxoo.com/player/1
      alias: 'about',
      component: Player,
      children:[
        {
            path: 'profile',
            component: PlayerProfile,
        },
        {
            path: 'stats',
            component: PlayerStats,
        },
      ]
    },
    {
      path: '/',
      name: 'Home',
      components: {
          myHeader: SettingHeader,
          mySideBar: SettingSideBar,
          myDetail: SettingDetail,
      }
    },
    {
      path: '/hi',  // 这里可以使用 * 通配
      name: 'hi',
      // 输入 http://xxxoo.com/hi  等同于输入 http://xxxoo.com/player/2 ,会自动重定向
      redirect: '/player/2',
    }
  ]
})

多参数路由

路由传递多个属性props

user.vue

<template>
    <div>
        <h1>
            测试url多参
        </h1>
        <p>uid={{ uid }}, nationality={{ nationality }}</p>
        <!-- 这里可以有上下两种方式去获取参数 -->
        <p>$route.params.uid={{ $route.params.uid }}</p>
        <p>$route.params.nationality={{ $route.params.nationality }}</p>
    </div>
</template>

<script>
export default {
    name: "User",
    // 这里表示从  params 里接受两个参数
    props: ['uid', 'nationality']
}
</script>

index.js 添加route

    {
      path: '/user/:uid/:nationality',
      name: 'User',
      component: User,
      props: true,
    }

App.vue

    <p>
      <router-link to="/user/1/USA">User1</router-link>
      <router-link to="/user/2/china">User2</router-link>
    </p>