vue前端界面框架

2018-06-07 fishedee 前端

1 概述

vue,前端界面框架

2 基础

npm install vue
npm install vue-loader --save

先安装好sublime text3的vue syntax 插件和npm关于vue的包

var webpack = require('webpack');

module.exports = {
  entry: './index.js',
  output: {
    path:'dist',
    filename: 'bundle.js'       
  },
  module:{
    loaders: [
      { test: /\.vue$/, loader: 'vue'},
      { test: /\.js$/, exclude: /node_modules/, loader: "babel-loader"},
      {test: /\.css?$/,loaders: ["style-loader","css-loader"],include: __dirname}
    ]
  },
  vue: {
   loaders: {
      js: 'babel'
    }
  },
  resolve: {
    alias: {
      'vue$': 'vue/dist/vue.common.js'
    },
    extensions: ['', '.js', '.css', '.scss','.vue'],
  },
  plugins:[
  ]
}

配置webpack关于vue文件的处理,以及vue中关于template的处理

<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Vue For Learn</title>
    </head>
    <body>
        <div id="app"><app></app></div>
        <script src="dist/bundle.js"></script>
    </body>
</html>

网页文件,注意有一个app的div标签,里面还有一个app的自定义标签

import Vue from 'vue'
import App from './src/app.vue'

new Vue({
 el: '#app',
 components: { App }
})

入口的index.js文件,就是引入app.vue组件,并挂载到#app位置上即可

<template>
<div class="message">{{ msg }}</div>
</template>

<script>
export default {
    data () {
        return {
            msg: 'Hello from vue-loader!'
        }
    }
}
</script>

<style>
.message {
 color: blue;
}
</style>

这是app.vue文件,很明显,这个组件式的写法,直接包含了html,style和script,非常的直接

Screen Shot 2016-12-18 at 9.59.32 P

启动后就能看到这个页面了,很明显,vue中的组件很好地解决了react中的style与html怎么独立引入组件的问题

3 生命周期

生命周期都挺直接简单的

4 模板语法

4.1 基础指令

<template>
<div>
    <!--(v-text)-->
    <div class="message">{{ msg }}</div>
    <!--(v-html)-->
    <div v-html="rawHtml"></div>
    <!--(v-bind)-->
    <div v-bind:id="dynamicId">动态属性</div>
    <!--(v-if)-->
    <p v-if="seen">Now you see me</p>
    <!--(v-on)-->
    <a v-on:click="doSomething">点我</a>
</div>
</template>

<script>
export default {
    data () {
        return {
            msg: 'Hello from vue-loader!',
            rawHtml:'<h1>I am html</h1>',
            dynamicId:'fishId10001',
            seen:(Math.random() > 0.5),

        }
    },
    methods:{
        doSomething(event){
            alert(event);
        }
    }
}
</script>

<style>
.message {
 color: blue;
}
</style>

绑定text,html,事件等等,一目了然

4.2 表达式

<template>
<div>
    <div>{{ number + 1 }}</div>
    <div>{{ ok ? 'YES' : 'NO' }}</div>
    <div>{{ message.split('').reverse().join('') }}</div>
</div>
</template>

<script>
export default {
    data () {
        return {
            number:1000,
            ok:(Math.random()>0.5),
            message:'fish',
        }
    }
}
</script>

<style>
.message {
 color: blue;
}
</style>

绑定的数据可以变为表达式,注意,每个绑定都只能包含单个表达式。不能做太复杂的逻辑,这个设计非常好

4.3 过滤器

<template>
<div>
    <div>{{ message | capitalize }}</div>
</div>
</template>

<script>
export default {
    data () {
        return {
            message:'fish',
        }
    },
    filters: {
        capitalize: function (value) {
          if (!value) return ''
          value = value.toString()
          return value.charAt(0).toUpperCase() + value.slice(1)
        }
    }
}
</script>

<style>
.message {
 color: blue;
}
</style>

使用bash中的管道语法,将message传入filter函数后输出,显然,管道是可以串联的

5 属性

5.1 普通属性

<template>
<div>
    <div>{{ number }}</div>
    <button v-on:click="add">增加</button>
</div>
</template>

<script>
export default {
    data () {
        return {
            number:1,
        }
    },
    methods: {
        add: function () {
          this.number ++;
        }
    }
}
</script>

<style>
.message {
 color: blue;
}
</style>

普通属性就是那些定义在data的属性,注意,这些属性在变更时,ui会自动刷新

<template>
<div>
    <div>{{ firstName }}</div>
    <div>{{ lastName }}</div>
    <div>{{ fullName }}</div>
    <button v-on:click="mod1">更改firstName</button>
    <button v-on:click="mod2">更改lastName</button>
</div>
</template>

<script>
export default {
    data () {
        return {
            firstName: 'Foo',
            lastName: 'Bar',
            fullName: 'Foo Bar'
        }
    },
    watch: {
        firstName: function (val) {
          this.fullName = val + ' ' + this.lastName
        },
        lastName: function (val) {
          this.fullName = this.firstName + ' ' + val
        }
    },
    methods:{
        mod1:function(){
            this.firstName += 'm';
        },
        mod2:function(){
            this.lastName += 'c';
        }
    }
}
</script>

<style>
.message {
 color: blue;
}
</style>

你可以watch这些属性的变化,当它们变化时,触发回调

5.2 计算属性

<template>
<div>
    <div>{{ firstName }}</div>
    <div>{{ lastName }}</div>
    <div>{{ fullName }}</div>
    <button v-on:click="mod1">更改firstName</button>
    <button v-on:click="mod2">更改lastName</button>
</div>
</template>

<script>
export default {
    data () {
        return {
            firstName: 'Foo',
            lastName: 'Bar',
        }
    },
    computed: {
        fullName: function () {
            return this.firstName + ' ' + this.lastName;
        }
    },
    methods:{
        mod1:function(){
            this.firstName += 'm';
        },
        mod2:function(){
            this.lastName += 'c';
        }
    }
}
</script>

<style>
.message {
 color: blue;
}
</style>

使用computed属性,当firstName或lastName发生变化时,computed属性也会改变。但是,要注意的是,computed依赖哪些属性是通过第一次时调用时确定的,这样的设计使得computed属性有缓存的能力,同时也埋下了坑

<template>
<div>
    <div>{{ firstName }}</div>
    <div>{{ lastName }}</div>
    <div>{{ fullName }}</div>
    <button v-on:click="mod1">更改firstName</button>
    <button v-on:click="mod2">更改lastName</button>
</div>
</template>

<script>
export default {
    data () {
        return {
            firstName: 'Foo',
            lastName: 'Bar',
            isFirst:true,
        }
    },
    computed: {
        fullName: function () {
            if( this.isFirst ){
                this.isFirst = false;
                return this.firstName;
            }else{
                return this.firstName + ' ' + this.lastName;
            }
        }
    },
    methods:{
        mod1:function(){
            this.firstName += 'm';
        },
        mod2:function(){
            this.lastName += 'c';
        }
    }
}
</script>

<style>
.message {
 color: blue;
}
</style>

第一次computed时只有firstName与isFirst参与,所以,后续修改lastName都不会触发computed属性的变更。

<template>
<div>
    <div>{{ firstName }}</div>
    <div>{{ lastName }}</div>
    <div>{{ fullName }}</div>
    <div>{{ fullNameLength }}</div>
    <button v-on:click="mod1">更改firstName</button>
    <button v-on:click="mod2">更改lastName</button>
</div>
</template>

<script>
export default {
    data () {
        return {
            firstName: 'Foo',
            lastName: 'Bar',
            isFirst:true,
        }
    },
    computed: {
        fullName: function () {
            return this.firstName + ' ' + this.lastName;
        },
        fullNameLength:function(){
            return this.fullName.length;
        }
    },
    methods:{
        mod1:function(){
            this.firstName += 'm';
        },
        mod2:function(){
            this.lastName += 'c';
        }
    }
}
</script>

<style>
.message {
 color: blue;
}
</style>

同时,计算属性支持依赖其他计算属性,这个非常棒,而且计算属性的依赖前后可以颠倒,只要这些computed之间不是形成环形依赖就可以了

6 样式

6.1 类

<template>
<div>
    <div v-bind:class="class1"></div>
    <div v-bind:class="[class2,class3]"></div>
    <div v-bind:class="{class4:isClass4Active,class5:isClass5Active}"></div>
    <div v-bind:class="class6"></div>
</div>
</template>

<script>
export default {
    data () {
        return {
            class1: 'class1Name',
            class2: 'class2Name',
            class3: 'class3Name',
            isClass4Active:true,
            isClass5Active:false,
        }
    },
    computed: {
        class6:function(){
            return {
                active: false,
                'text-danger': true
            };
        }
    }
}
</script>

<style>
.message {
 color: blue;
}
</style>

v-bind:class做了特定的语法糖,支持单class,多class(数组),多控制class(object)

6.2 样式

<template>
<div>
    <div v-bind:style="style1">Hello Fish1</div>
    <div v-bind:style="[style2,style3]">Hello Fish1</div>
    <div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }">Hello Fish2</div>
</div>
</template>

<script>
export default {
    data () {
        return {
            style1:{
                color:'red',
                fontSize:'10px',
            },
            style2:{
                color:'blue',
            },
            style3:{
                fontSize:'30px',
            },
            activeColor: 'yellow',
            fontSize: '20',
        }
    }
}
</script>

<style>
.message {
 color: blue;
}
</style>

跟class一样,支持单style,多style,以及映射style。而且,vue的style依然是驼峰写法,并且加入了自动的浏览器前缀,这个跟React.createStyle实在太像了。

7 条件渲染

7.1 多条件

<template>
<div>
    <div v-if="type === 'A'">
      A
    </div>
    <div v-else-if="type === 'B'">
      B
    </div>
    <div v-else-if="type === 'C'">
      C
    </div>
    <div v-else>
      Not A/B/C
    </div>
    <input v-model="type"/>
</div>
</template>

<script>
export default {
    data () {
        return {
            type:'A',
        }
    }
}
</script>

<style>
.message {
 color: blue;
}
</style>

简单的v-if,v-else-if与v-else的用法

7.2 key重用

<template>
<div>
    <template v-if="loginType === 'username'">
      <label>Username</label>
      <input placeholder="Enter your username">
    </template>
    <template v-else>
      <label>Email</label>
      <input placeholder="Enter your email address">
    </template>
    <button v-on:click="toggle">toggle</button>
</div>
</template>

<script>
export default {
    data () {
        return {
            loginType:'username',
        }
    },
    methods:{
        toggle:function(){
            this.loginType = (this.loginType=='username'?'mail':'username');
        }
    }
}
</script>

<style>
.message {
 color: blue;
}
</style>

在输入时会发现切换后vue依然沿用了原来的input控件,导致input控件重用

<template>
<div>
    <template v-if="loginType === 'username'">
      <label>Username</label>
      <input placeholder="Enter your username" key="1">
    </template>
    <template v-else>
      <label>Email</label>
      <input placeholder="Enter your email address" key="2">
    </template>
    <button v-on:click="toggle">toggle</button>
</div>
</template>

<script>
export default {
    data () {
        return {
            loginType:'username',
        }
    },
    methods:{
        toggle:function(){
            this.loginType = (this.loginType=='username'?'mail':'username');
        }
    }
}
</script>

<style>
.message {
 color: blue;
}
</style>

解决办法就是加入key,区别不同的input,很明显,vue采用的virutal dom比较算法跟react是一样的,抄袭得有点明显呀

7.3 show

<template>
<h1 v-show="ok">Hello!</h1>
</template>

<script>
export default {
    data () {
        return {
            ok:true,
        }
    }
}
</script>

<style>
.message {
 color: blue;
}
</style>

使用show控制是否显示,这里是通过display来实现的,而不是if的dom插入删除来实现。

8 列表渲染

8.1 基础循环

<template>
    <ul id="example-2">
      <li v-for="(item, index) in items">
        {{ parentMessage }} - {{ index }} - {{ item.message }}
      </li>
    </ul>
</template>

<script>
export default {
    data () {
        return {
            parentMessage: 'Parent',
            items: [
              { message: 'Foo' },
              { message: 'Bar' }
            ]
        }
    }
}
</script>

<style>
.message {
 color: blue;
}
</style>

使用v-for的循环,简单,暴力

8.2 key

<template>
    <ul id="example-2">
      <li v-for="(item, index) in items" key="item.id">
        {{ parentMessage }} - {{ index }} - {{ item.message }}
      </li>
    </ul>
</template>

<script>
export default {
    data () {
        return {
            parentMessage: 'Parent',
            items: [
              { message: 'Foo' ,id:'10001'},
              { message: 'Bar' ,id:'10002'}
            ]
        }
    }
}
</script>

<style>
.message {
 color: blue;
}
</style>

使用key来加快列表的重新渲染,这里也是同样和react一样

8.3 数组更新检测

push()
pop()
shift()
unshift()
splice()
sort()
reverse()

数组的以上方法触发后,会触发重新渲染

vm.items[indexOfItem] = newValue
vm.items.length = newLength

但是这样并不能触发数组的变化,所以,数组的元素不能为基本元素,只能为Object

9 事件绑定

9.1 基本事件

<template>
    <div id="example-1">
      <button v-on:click="counter += 1">增加 1</button>
      <p>这个按钮被点击了 {{ counter }} 次。</p>
    </div>
</template>

<script>
export default {
    data () {
        return {
            counter:1
        }
    }
}
</script>

<style>
.message {
 color: blue;
}
</style>

把事件直接写到v-on上,内联事件绑定

<template>
    <div id="example-1">
      <button v-on:click="onclick">增加 2</button>
      <p>这个按钮被点击了 {{ counter }} 次。</p>
    </div>
</template>

<script>
export default {
    data () {
        return {
            counter:1
        }
    },
    methods:{
        onclick:function(event){
            this.counter += 2;
            console.log(event);
        }
    }
}
</script>

<style>
.message {
 color: blue;
}
</style>

将事件写入到methods上,方法事件绑定,注意方法的第一个参数可以获取到事件信息

9.2 捆绑事件

<template>
    <div id="example-3">
      <button v-on:click="say('hi',$event)">Say hi</button>
      <button v-on:click="say('what',$event)">Say what</button>
    </div>
</template>

<script>
export default {
    data () {
        return {
            counter:1
        }
    },
    methods:{
        say:function(mm,event){
            console.log(mm);
            console.log(event);
        }
    }
}
</script>

<style>
.message {
 color: blue;
}
</style>

调用时加入特定的参数,注意可以用$event来获取事件信息

9.3 事件修饰符

.stop
.prevent
.capture
.self
.once

可以在事件后加上这些修饰符

<template>
    <div id="example-3" v-on:click="doAll">
      <button v-on:click.stop="doThis">click me</button>
    </div>
</template>

<script>
export default {
    data () {
        return {
            counter:1
        }
    },
    methods:{
        doAll:function(){
            console.log("doAll");
        },
        doThis:function(){
            console.log("doThis");
        }
    }
}
</script>

<style>
.message {
 color: blue;
}
</style>

stop阻止了事件冒泡

10 控件绑定

10.1 基本绑定

<template>
    <div>
        <input v-model="message" placeholder="edit me">
        <p>Message is: {{ message }}</p>
    </div>
</template>

<script>
export default {
    data () {
        return {
            message:'mm'
        }
    }
}
</script>

<style>
.message {
 color: blue;
}
</style>

略显神奇的v-model,当input输入时,会直接修改this.message,而进一步造成控件渲染,实现了MVVM的杀手功能双向绑定。其实实现原理很简单,平时的v-bind只是data->view的映射,v-model只是加上了view->data的映射而已

10.2 绑定修饰

<template>
    <div>
        <input v-model.lazy="message" placeholder="edit me">
        <p>Message is: {{ message }}</p>
    </div>
</template>

<script>
export default {
    data () {
        return {
            message:'mm'
        }
    }
}
</script>

<style>
.message {
 color: blue;
}
</style>

加入lazy修饰符,当控件丢失焦点时才会触发修改data

11 组件化

代码

11.1 属性与事件

<template>
    <div>
        <h1>{{title}}</h1>
        <button v-on:click="add">You clicked me {{ count }} times.</button>
    </div>
</template>

<script>
export default {
    props: ['title'],
    data () {
        return {
            count:0,
        }
    },
    methods: {
        add: function () {
          this.count++
          this.$emit('click')
          this.$emit('change',this.count)
        }
    }
}
</script>

<style>
</style>

这是counter组件的代码,跟普通的vue一样,不同的地方有两个,组件提供属性和事件的控制,属性需要增加props,事件在触发时使用this.$emit就可以了。

<div v-for="counter,index in counters" v-bind:key="counter.id">
    <counter v-bind:title="counter.title" v-on:click="addAllClick" v-on:change="change(index,$event)"/>
</div>

使用组件时就和使用普通的组件一样,属性用v-bind传递,事件用v-on传递。

<template>
    <div class="alert-box">
        <strong>Error!</strong><slot/>
    </div>
</template>

<script>
export default {}
</script>

<style>
.alert-box{
    padding: 10px 20px;
    background: #f3beb8;
    border: 1px solid #f09898;
}
.alert-box strong{
    font-weight:600;
}
</style>

这是alert组件,slot的用法就像react中的this.props.children。

<alert>Something bad is happend</alert>

使用时将文本或其他组件扔进去就可以了。

11.2 注册

import counter from './counter.vue'
vue.component('counter', counter)

组件有两种注册方式,全局注册和局部注册,全局注册就是上面这一种,一旦全局注册后,任意地方的template都可以用名字来直接引用这个组件。

import alert from './alert.vue'
export default {
    data(){return xxx}
    ....
    components:{
        alert
    }
}

局部注册就是在使用这个组件时,将组件放入到components中,注册的名字就是components中key的名字。

11.3 动态组件

<component v-bind:is="currentTabComponent"/>

vue中提供了动态组件的方法,就是通过组件名字来生成这个组件,用v-bind:is就可以了。

12 总结

vue的优点:

  • html,css与js比react更优雅地结合在一个文件上。react的代替品是jsx,createStyleSheet和js,明显更直观简单。
  • 不需要setState,直接修改数据就能刷新页面,而且不需要react的shouldComponentUpdate就能实现最高效的渲染路径。
  • 渐进式的开发模式,模版方式->组件方式->路由整合->数据流整合->服务器渲染。上手的曲线更加平滑简单,而且不像react一上来就是组件全家桶
  • v-model给开发后台管理系统带来极大的便利,反观用react开发后台就是个杯具
  • 自带keep-alive优化,解决react中切换页面后状态丢失的问题,这个实在是漂亮。

缺点:

  • vue 2.0后渲染方式明显与react的virtual dom一个算法,抄袭有点明显
  • data的观察渲染模式虽然高效,但很多坑,数组不能为基础类型,必须为复合类型,对象增减属性也不会触发渲染
  • 组件的属性传递没有react的直观和明显
  • script与template是分离的,而react的script与template是一体的,这增加了太多的概念。同样的道理,vue中事件与属性是分离的,而react中属性是函数时,就当事件来用就好了,就这么简单。

所以,如果你想开发后台系统或小页面时,vue是个很好的选择。但是想开发庞大的单页面时,react在可靠性与直观度上其实更优。

参考资料

相关文章