简单Vue实例

el:DOM的负责区域

data:定义数据

<script>
    var app = new Vue({
		el: '#app',
		data: {
			content:'hello world'
		}
	})
</script>

Vue生命周期

生命周期函数就是Vue实例在某一个时间点会自动执行的函数

官方生命周期钩子文档

v-for 循环

<ul>
    <li v-for="item of list">{{item}}</li>
</ul>

<script>
	var app = new Vue({
        el: '#app',
        data: {
            list: ['111','222','333']
        }
    })
</script>

v-on 事件绑定、监听

使用v-on,引号内为表达式或者变量

"v-on:click" 可写作 "@click"

methods:方法

<button v-on:click="handleBtnClick">
    提交
</button>

<script>
	var app = new Vue({
        el: '#app',
        methods: {
            handleBtnClick: function() {
                alert('click')
            }
        }
    })
</script>

v-model 双向数据绑定

<input type="text" v-model="inputValue" />

<script>
    var app = new Vue({
        el: '#app',
        data: {
            inputValue: ''
        }
    })
</script>

v-bind 属性绑定,简写:


v-text和v-html

v-text 文本绑定,自动转义,纯文本

v-html 不转义,做解析

<div id="app">
    <div v-text="name"></div>
    <div v-html="name"></div>
</div>

<script>
	var vm = new Vue({
        el: "#app",
        data: {
            name: "<h1>hello world</h1>"
        }
    })
</script>

效果:

image-20210406225012554

计算属性 computed

如果依赖的属性(firstName 和 lastName)不变,则使用上一次计算的缓存,提高性能

<div id="#app">
    <div>
        {{fullName}}
        {{age}}
    </div>
</div>

<script>
	var vm = new Vue({
        el: '#app',
        data: {
            firstName: 'Solitary',
            lastName: 'Su'
            age: "20"
        },
        computed: {
            fullName: function() {
                return this.firstName + ' ' + this.lastName
            }
        }
    })
</script>

getter和setter方法

<div id="#app">
    <div>
        {{fullName}}
        {{age}}
    </div>
</div>

<script>
	var vm = new Vue({
        el: '#app',
        data: {
            firstName: 'Solitary',
            lastName: 'Su'
            age: "20"
        },
        computed: {
            fullName: {
                get: function() {
                    return this.firstName + ' ' + this.lastName
                }
                set: function(value) {
            		console.log(value)
        		}
            }
        }
    })
</script>

监听器 watch

如果watch,computed都可以实现,建议使用computed,性能高

<div id="app">
    <div>
        {{fullName}}
    </div>
</div>

<script>
	var vm = new Vue({
        el: '#app',
        data: {
            firstName: 'Solitary',
            lastName: 'Su',
            fullName: 'Solitary Su',
        },
        watch: {
            firstName: function() {
                this.fullName = this.firstName + " " + this.lastName;
            },
            lastName: function() {
                this.fullName = this.firstName + " " + this.lastName;
            }
        }
    })
</script>

样式绑定

方式1:对象绑定

<div id="app">
    <div @click="handleDivClick" :class="{activated: isActivated}">
        Hello world
    </div>
</div>

<script>
    var vm = new Vue({
        el: "#app",
        data: {
            isActivated: false,
        },
        methods: {
            handleDivClick: function () {
                this.isActivated = !this.isActivated;
            },
        },
    });
</script>

方式2:数组绑定

<div id="app">
    <div @click="handleDivClick" :class="[activated]">Hello world</div>
</div>

<script>
    var vm = new Vue({
        el: "#app",
        data: {
            activated: "",
        },
        methods: {
            handleDivClick: function () {
                this.activated = this.activated === "activated" ? "" : "activated";
            },
        },
    });
</script>

方式3:style操作

<div id="app">
    <div :style="[styleObj, {fontSize: '20px'}]" @click="handleDivClick">Hello world</div>
</div>

<script>
    var vm = new Vue({
        el: "#app",
        data: {
            styleObj: {
                color: "black",
            },
        },
        methods: {
            handleDivClick: function () {
                this.styleObj.color =
                    this.styleObj.color === "red" ? "black" : "red";
            },
        },
    });
</script>

v-if和v-show

<div id="app">
    <div v-if="show">{{message}}</div>
    <div v-show="show">{{message}}</div>
</div>

<script>
    var vm = new Vue({
        el: "#app",
        data: {
            show: false,
            message: "Hello world"
        },
    })
</script>

v-if和v-show区别:

  • v-if,如果是false,从DOM中移除

  • v-show,如果是false,会被css被隐藏

频率高用v-show,不会销毁DOM,性能更高

频率不高用v-if

v-else、v-else-if

<div id="app">
    <div v-if="show === 'a'">This is A</div>
    <div v-else-if="show === 'b'">This is B</div>
    <div v-else>This is others</div>
</div>

<script>
    var vm = new Vue({
        el: "#app",
        data: {
            show: "a",
            message: "Hello World"
        },
    })
</script>

key

vue渲染过程中,尽量复用原有元素。vue根据key值识别唯一元素。

列表渲染

不推荐使用index作为key值,通常后端向前端传输数据会有唯一的标识符。

数组循环:

<div id="app">
    <div v-for="(item, index) of list" :key="item.id">
        {{item.text}} ---- {{index}}
    </div>
</div>

<script>
    var vm = new Vue({
        el: "#app",
        data: {
            list: [
                {
                    id: "01010101",
                    text: "hello",
                },
                {
                    id: "01010102",
                    text: "solitary",
                },
                {
                    id: "01010103",
                    text: "su",
                },
            ],
        },
    });
</script>

尝试修改数组内容时,不能使用下标修改,即vm.list[3] = {}

vue提供7种变异方法:

  • pop 删除
  • push 增加
  • shift 第一项删除
  • unshift 第一项增加内容
  • splice 数组截取
  • sort 排序
  • reverse 取反
vm.list.splice(1, 1, {id:"333", text: "solitary1"})
vm.list = [
    {
        id: "01010101",
        text: "hello",
    },{
        id: "01010102",
        text: "solitary1",
    },
    {
        id: "01010103",
        text: "su",
    }]

对象循环:

<div id="app">
    <div v-for="(item, key, index) of userInfo">
        {{item}} --- {{key}} --- {{index}}
    </div>
</div>

<script>
    var vm = new Vue({
        el: "#app",
        data: {
            userInfo: {
                name: "solitary",
                age: 20,
                gander: "male",
                salary: "secret"
            }
        },
    });
</script>

对象动态加值不可行

vm.userInfo = {
    name: "solitary",
    age: 20,
    gander: "male",
    salary: "secret",
    address: "beijing"
}

template 占位符

template标签不出现在html中

<div id="app">
    <template v-for="(item, index) of list" :key="item.id">
        <div>{{item.text}} ---- {{index}}</div>
        <span> {{item.text}} </span>
    </template>
</div>
<script>
    var vm = new Vue({
        el: "#app",
        data: {
            list: [
                {
                    id: "01010101",
                    text: "hello",
                },
                {
                    id: "01010102",
                    text: "solitary",
                },
                {
                    id: "01010103",
                    text: "su",
                },
            ],
        },
    });
</script>

Vue中的set方法

Vue.set(vm.userInfo,"address","beijing")
vm.$set(vm.userInfo,"address","beijing")

is属性

<div id="root">
    <table>
        <tbody>
            <tr is="row"></tr>
            <tr is="row"></tr>
            <tr is="row"></tr>
        </tbody>
    </table>
</div>
<script>
    Vue.component("row", {
        template: "<tr><td>this is a row</td></tr>",
    });
    var vm = new Vue({
        el: "#root",
    });
</script>

符合h5编码规范

子组件定义data使用函数

在根组件里面,使用对象方式定义data没有问题,子组件不能这么定义。子组件要求是函数,不能是对象。每个子组件拥有独立的数据存储,不会出现多个子组件互相影响。

Vue.component("counter", {
	data: function() {
		return {
			number: 0
		}
	}
})

通过ref操作DOM

this.$refs.ref

<div id="root">
    <div ref="hello" @click="handleClick">hello world</div>
</div>

<script>
    var vm = new Vue({
        el: "#root",
        methods: {
            handleClick: function() {
                this.$refs.hello.innerHTML = "123"
            }
        }
    });
</script>

子组件向父组件传值

<counter @change="handleChange"></counter>

this.$emit("change", this.content)

父子组件的数据传输

父组件向子组件传值

<div id="root">
    <counter :count="0"></counter>
    <counter :count="1"></counter>
</div>

<script>
    var counter = {
        props: ["count"],
        data: function () {
            return {
                number: this.count,
            };
        },
        template: "<div @click='handleClick'>{{number}}</div>",
        methods: {
            handleClick: function () {
                this.number++;
            },
        },
    };
    var vm = new Vue({
        el: "#root",
        components: {
            counter: counter,
        },
    });
</script>

单向数据流,父组件可以通过属性的形式给子组件传递数据,父组件可以进行随意修改,子组件不能修改父组件的参数。

因为如果接受的是Object形式,子组件修改后,如果被其他子组件使用,对其他子组件造成影响。

子组件向父组件传值

<div id="root">
    <counter :count="3" @inc="handleIncrease"></counter>
    <counter :count="2" @inc="handleIncrease"></counter>
    <div>{{total}}</div>
</div>

<script>
    var counter = {
        props: ["count"],
        data: function () {
            return {
                number: this.count,
            };
        },
        template: "<div @click='handleClick'>{{number}}</div>",
        methods: {
            handleClick: function () {
                this.number++;
                this.$emit("inc", 1);
            },
        },
    };
    var vm = new Vue({
        el: "#root",
        data: {
            total: 5,
        },
        components: {
            counter: counter,
        },
        methods: {
            handleIncrease: function (step) {
                this.total += step;
            },
        },
    });
</script>

组件参数校验

<div id="root">
    <child :content="'123'"></child>
</div>
<script>
    Vue.component("child", {
        props: {
            content: {
                // 类型
                type: String,
                // 是否需要传值
                required: false,
                // 默认值
                default: "default value",
                // 自定义校验器
                validator: function(value) {
                    return (value.length > 5)
                }
            },
        },
        template: "<div>{{content}}</div>",
    });
    var vm = new Vue({
        el: "#root",
    });
</script>

props特性与非props特性

props特性:

​ 当父组件使用子组件时,通过属性向子组件传值,恰好子组件声明了对父组件传递的接收,即父组件传递了content,子组件声明了props: content,具有对应关系。

特点:

  • 不会在DOM标签中显示
  • 在子组件可以通过插值表达式或者this取得内容,显示出来

非props特性

父组件向子组件传递属性,但是没有声明。

  • 没法获取内容
  • 会显示在HTML属性中

给组件绑定原生事件

正常监听内部组件向外触发的自定义事件,因此需要两层传递

<div id="root">
    <child @click="handleClick"></child>
  </div>
  <script>
    Vue.component("child", {
      template: "<div @click='handleChildClick'>Child</div>",
      methods: {
        handleChildClick: function() {
          this.$emit("click")
        }
      }
    })

    var vm = new Vue({
      el: "#root",
      methods: {
        handleClick: function() {
          alert("233")
        }
      }
    })
  </script>

监听原生点击事件 @click.native

<div id="root">
    <child @click.native="handleClick"></child>
  </div>
  <script>
    Vue.component("child", {
      template: "<div>Child</div>"
    })

    var vm = new Vue({
      el: "#root",
      methods: {
        handleClick: function() {
          alert("233")
        }
      }
    })
  </script>

非父子组件间的传值(bus/总线/发布订阅模式/观察者模式)

解决方法:

  • Vuex
  • 发布定义模式,总线机制
<div id="root">
    <child content="Solitary"></child>
    <child content="Su"></child>
</div>
<script>
    Vue.prototype.bus = new Vue();

    Vue.component("child", {
        data: function () {
            return {
                selfContent: this.content,
            };
        },
        props: {
            content: String,
        },
        template: "<div @click='handleClick'>{{selfContent}}</div>",
        methods: {
            handleClick: function () {
                this.bus.$emit("change", this.selfContent);
            },
        },
        mounted: function () {
            var this_ = this;
            this.bus.$on("change", function (msg) {
                this_.selfContent = msg;
            });
        },
    });

    var vm = new Vue({
        el: "#root",
    });
</script>

在Vue中使用插槽(slot)

content不好用的时候,部分内容需要外部传递。

<div id="root">
    <body-content>
        <div class="header" slot="header">header</div>
        <div class="footer" slot="footer">footer</div>
    </body-content>
</div>

<script>
    Vue.component("body-content", {
        template: `<div>
                       <slot name="header">
                           <h1>
                               default header
                           </h1>
                       </slot>
                       <div class='content'>content</div>
                       <slot name="footer"></slot>
                   </div>`,
    });

    var vm = new Vue({
        el: "#root",
    });
</script>

Vue中的作用域插槽

传递作用域插槽,必须由<template>包裹,声明子组件接收的数据放在哪,告诉子组件模板信息。

子组件做循环,或者dom结构由外部结构传进来的时候可以使用。子组件可以向父组件插槽传数据,父组件接收数据必须使用template和slot-scope。

<div id="root">
      <child>
        <template slot-scope="props">
          <li>{{props.item}} - hello</li>
        </template>
      </child>
    </div>

    <script>
      Vue.component("child", {
        data: function () {
          return {
            list: [1, 2, 3, 4],
          };
        },
        template: `
          <div>
            <ul>
              <slot v-for="item of list" :item=item></slot>
            </ul>
          </div>
        `,
      });

      var vm = new Vue({
        el: "#root",
      });
</script>

动态组件(component标签)

使用一个按钮,使得显示内容不断变化

使用v-if

<div id="root">
    <child-one v-if="type === 'child-one'"></child-one>
    <child-two v-if="type === 'child-two'"></child-two>
    <button @click="handleBtnClick">change</button>
</div>

<script>
    Vue.component("child-one", {
        template: "<div>child-one</div>",
    });
    Vue.component("child-two", {
        template: "<div>child-two</div>",
    });
    var vm = new Vue({
        el: "#root",
        data: {
            type: "child-one",
        },
        methods: {
            handleBtnClick: function () {
                this.type = this.type === "child-one" ? "child-two" : "child-one";
            },
        },
    });
</script>

使用component标签

<div id="root">
    <component :is="type"></component>
    <button @click="handleBtnClick">change</button>
</div>

<script>
    Vue.component("child-one", {
        template: "<div>child-one</div>",
    });
    Vue.component("child-two", {
        template: "<div>child-two</div>",
    });
    var vm = new Vue({
        el: "#root",
        data: {
            type: "child-one",
        },
        methods: {
            handleBtnClick: function () {
                this.type = this.type === "child-one" ? "child-two" : "child-one";
            },
        },
    });
</script>

v-once指令

可以把反复销毁的组件放入内存,提高性能

<div id="root">
    <child-one v-if="type === 'child-one'"></child-one>
    <child-two v-if="type === 'child-two'"></child-two>
    <button @click="handleBtnClick">change</button>
</div>

<script>
    Vue.component("child-one", {
        template: "<div v-once>child-one</div>",
    });
    Vue.component("child-two", {
        template: "<div v-once>child-two</div>",
    });
    var vm = new Vue({
        el: "#root",
        data: {
            type: "child-one",
        },
        methods: {
            handleBtnClick: function () {
                this.type = this.type === "child-one" ? "child-two" : "child-one";
            },
        },
    });
</script>

环境准备

下载安装nodejs:http://nodejs.cn/download/

教程:https://www.cnblogs.com/aizai846/p/11441693.html

构建Vue项目并运行

npm install -g vue-cli
vue init webpack my-app # 创建项目
cd my-app
npm run dev

创建的时候会问一堆问题:

Project name ==> 项目名称,直接回车

Project description ==> 项目描述,可以直接回车

Author ==> 作者,也可以直接回车

Vue build (Use arrow keys) ==> 构建方式,选 Runtime + Compiler: recommended for most users,即直接回车

Install vue-router? (Y/n) ==> 是否需要路由,输入Y回车

Use ESLint to lint your code? ==> 是否使用ESLint语法检测,输入Y回车

Pick an ESLint preset (Use arrow keys) ==> 直接回车

Set up unit tests ==> 是否安装单元测试工具,目前我们不需要,所以n回车

Setup e2e tests with Nightwatch ==> 是否需要端到端测试工具,目前我们不需要,所以n回车

Should we run npm install for you after the project has been created? ==> 我用的npm

项目目录

  • static目录:静态资源,图片,json数据

  • node_modules目录:第三方node包

  • src目录:源代码

    • main.js文件:整个项目的入口文件
    • app.vue文件:根组件
    • router目录:路由
    • components目录:小组件
    • pages目录:自行创建,每个文件夹对应单个页面
    • assets目录:图片资源
  • config目录:项目配置文件,基础配置信息放在index.js,开发环境配置信息放在dev.env.js,线上环境的配置信息放在prod.env.js

  • build目录:webpack打包的配置内容

Vue单文件组件

vue结尾文件,实际上放的是一个vue组件

Vue中的路由

路由就是根据网址的不同,返回不同的内容给用户

<router-view> 显示的是当前路由地址所对应的内容

<router-link> 跳转,原理为在div外部套a标签,可用css强制变色

多页应用和单页应用

多页应用

原理:页面跳转 → 返回HTML

优点:首屏时间快,SEO效果好

缺点:页面切换慢

单页应用

原理:页面跳转 → JS渲染

优点:页面切换快

缺点:首屏时间稍慢,SEO差

开发过程

移动端开发项目初始化

配置index.html中的viewport

<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">

引入reset.css:重置页面样式表,解决不同手机浏览器样式不同的问题

引入boder.css:解决多倍屏1像素边框问题

引入fastclick库:解决点击延迟问题:

npm install fastclick --save

main.js

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import fastClick from 'fastclick'
import './assets/styles/reset.css'
import './assets/styles/border.css'

Vue.config.productionTip = false
fastClick.attach(document.body)

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})

ajax

使用axios

npm install axios --save

在static目录下创建mock文件夹,添加index.json

一般来说本地json不上传git,在.gitignore文件,上半部分加入

static/mock

方便上线,加入转发机制,由webpack dev工具提供

打开config/index.js

dev:proxyTable

proxyTable: {
    '/api': {
        target: 'http://localhost:8080',
            pathRewriter: {
                '^/api': '/static/mock'
            }
    }
}

Vuex

在src下创建store文件夹,新建index.js

内网穿透

出现 Invalid Host header

打开build目录下的webpack.base.config.js

devServer: {
    disableHostCheck: true,
},

低版本安卓出现白屏

可能1:

npm install babel-polyfill --save

可能2:dev server的问题

Q.E.D.


我还有很多想要完成的梦想。