5

vue学习记录

 2 years ago
source link: https://duzhi5368.github.io/2020/05/vue%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

vue学习记录

五月 28, 2020

发布在 程序, 2020

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

下面是一些基本的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了
[email protected] | MIT | deps: 20 | versions: 35
dist-tags:
latest: 2.9.6
published a year ago by yyx990803 <[email protected]>

c:\Users\freeknight>npm install -g [email protected]    // 下载vue
+ [email protected]
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******[email protected]                 // 开发者邮箱确认
? 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”} 的这个 ^ 符号,表示版本指定,不允许智能调整。
+ [email protected]
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
+ [email protected]
+ [email protected]
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>

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK