Vue.js起步
Vue.js是一个MVVM的前端框架,与React.js相似,都注重解决view层开发痛点。对于具备开发经验的同学,不建议从文档的开头进行学习,我们完全可以通过vue-cli
脚手架开始搭建学习。
下载vue-cli
cnpm install vue-cli -g
使用
vue-cli为我们提供了多种构建模板,我们在正式开发中,一般都会选择webpack
,顾名思义,就是用webpack进行打包。
vue init webpack vue-demo
Project name vue-demo
Project description A Vue.js project
Author liuhao <liuhao@limefamily.com>
Vue build standalone
Install vue-router? Yes
Use ESLint to lint your code? No
Set up unit tests No
Setup e2e tests with Nightwatch? No
Should we run `npm install` for you after the project has been created? (recom
mended) no
vue-cli会询问初始化项目的可选参数,基于现在的状态,我们只需要在install vue-router
时选择Yes
即可。随后vue-cli会自动npm install
进行下载。
开启server
package下载完毕后,我们可以先看一下模板为我们提供的package.json里的scripts
。需要指出的是,这是node或者npm项目快速了解项目的通用套路
。
cat package.json
"scripts": {
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
"start": "npm run dev",
"build": "node build/build.js"
}
我们可以看到,这里我们就要运用webpack
相关的知识,对webpack不了解的同学可以去看我的另外一篇介绍webpack的文章:
对webpack没有压力的同学我们可以继续往下走,scripts
里提供了三条命令供我们使用:
- dev用于开启
webpack-dev-server
,具备热修改的功能。 - start和dev效果相同。
- build用于编译打包bundle.js和静态资源。使用到了node命令,具体可见build.js脚本。
我们尝试使用start命令:
cnpm start
Your application is running here: http://localhost:8080
如果出现上述反馈,说明start成功,我们通过浏览器访问8080端口,可以看到welcome to vue.js app
。
以上就是vue.js的快速入门。我们通过脚手架成功在本地开启了一个server,利用模板生成的代码,显示出了一个hello world。
Vue.js基础要点
Vue.js作为一个渐进式的框架,起步学习阶段需要了解的可能并不需要太多,我们只需要掌握一些最基础的内容就能运用起来,比如:
- vue实例
- vue指令
- 事件处理
- vue自定义组件
- vue单文件组件
- vue生命周期
- vue-router
以上内容是必须弄懂的,也是在开发中最常用的知识点。我们顺序学习。为了方便学习基础知识,我们还是通过标签引入的方式来学习,这样更方便演示。学习完基础知识后,我们再回到vue-cli
上,就可以直接上手开发了。
创建项目并创建index.html
mkdir vue-basic
cd vue-basic
touch index.html
编写如下代码:
<!DOCTYPE html>
<html>
<head>
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>
<body>
<div id="app">
{{message}}
</div>
<script type="text/javascript">
new Vue({
el:'#app',
data : {
message:'hello vue.js'
}
});
</script>
</body>
</html>
我们通过cdn引入vue,这个url也是官方推荐使用的。我们首先创建一个div并给id赋值,在script中通过new Vue()的方式
与div
进行挂载。
注意el:'#app'
起的就是这个作用,在data
中我们可以初始化需要的变量,以对象的key value
方式。在html id app
中的任何一个位置,我们都可以使用{{var}}
的方式得到变量的值。
指令
vue中指令都有自己的使用场景,我们将每个指令的作用都过一遍。
v-text
<!DOCTYPE html>
<html>
<head>
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>
<body>
<div id="app">
{{message}}
<p v-text="text"></p>
<p>{{text}}</p>
</div>
<script type="text/javascript">
new Vue({
el:'#app',
data : {
message:'hello vue.js',
text : 'this is text string'
}
});
</script>
</body>
</html>
通过浏览器预览,我们可以认为,v-text
和我们通过{{var}}
产生的效果是一样的。
v-html
需要用到v-html
这个指令的原因是我们如果在变量中存储的是html字符串,在浏览器中会原样输出,所以为了能够将html字符串在浏览器中正常显示,我们就需要使用v-html
:
<!DOCTYPE html>
<html>
<head>
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>
<body>
<div id="app">
{{message}}
<p v-text="text"></p>
<p>{{text}}</p>
<div>{{html}}</div> //错误用法
<div v-html="html"></div> //正确用法
</div>
<script type="text/javascript">
new Vue({
el:'#app',
data : {
message:'hello vue.js',
text : 'this is text string',
html : '<div>hello html</div>'
}
});
</script>
</body>
</html>
可以认为,希望输出html并正常显示内容的话,第一种使用方式是错误的,应该使用v-html
指令。
v-show
顾名思义,就是用来控制组件显示还是隐藏的。
<!DOCTYPE html>
<html>
<head>
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>
<body>
<div id="app">
{{message}}
<p v-text="text"></p>
<p>{{text}}</p>
<div>{{html}}</div>
<div v-html="html"></div>
<div v-show="show">visible</div>
</div>
<script type="text/javascript">
new Vue({
el:'#app',
data : {
message:'hello vue.js',
text : 'this is text string',
html : '<div>hello html</div>',
show :true
}
});
</script>
</body>
</html>
可选值为true
和flase
,为true时,组件正常显示,为flase时,组件将被隐藏。需要注意的是,此时的原理是div被设置了display:none
。
v-if
系列
v-if
也是控制组件的显示与隐藏,与v-show
不同的是,v-if
的处理方式是直接在html把div给注释掉了。本质不同是,v-show
被隐藏还在dom中,v-if
被隐藏后将脱离dom。
<!DOCTYPE html>
<html>
<head>
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>
<body>
<div id="app">
{{message}}
<p v-text="text"></p>
<p>{{text}}</p>
<div>{{html}}</div>
<div v-html="html"></div>
<div v-show="show">invisible</div>
<div v-if="is_show">testtest</div>
</div>
<script type="text/javascript">
new Vue({
el:'#app',
data : {
message:'hello vue.js',
text : 'this is text string',
html : '<div>hello html</div>',
show :false,
is_show:false
}
});
</script>
</body>
</html>
v-for
v-for
应该是开发最常用的指令了,一般在ul li
中配合使用,循环创建相同的组件。
<!DOCTYPE html>
<html>
<head>
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>
<body>
<div id="app">
{{message}}
<p v-text="text"></p>
<p>{{text}}</p>
<div>{{html}}</div>
<div v-html="html"></div>
<div v-show="show">invisible</div>
<div v-if="is_show">testtest</div>
<ul>
<li v-for="user in users">
{{user['name']}}
</li>
</ul>
</div>
<script type="text/javascript">
new Vue({
el:'#app',
data : {
message:'hello vue.js',
text : 'this is text string',
html : '<div>hello html</div>',
show :false,
is_show:false,
users:[
{
id:1,
name:'zhangsan'
},
{
id:2,
name:'lisi'
},
{
id:3,
name:'wangwu'
}
]
}
});
</script>
</body>
</html>
浏览器预览为:
- zhangsan
- lisi
- wangwu
v-on
用来处理事件监听,比较常见的是处理点击事件。在介绍事件之前,我们先来了解一下和data
同级的methods
。在data中允许我们定义变量,在methods中允许我们定义函数。结构如下:
new Vue({
data:{
num:number;
str:'string';
},
methods:{
call(){
console.log('call');
}
}
});
了解了methods之后,我们再来看事件监听:
<!DOCTYPE html>
<html>
<head>
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>
<body>
<div id="app">
{{message}}
<p v-text="text"></p>
<p>{{text}}</p>
<div>{{html}}</div>
<div v-html="html"></div>
<div v-show="show">invisible</div>
<div v-if="is_show">testtest</div>
<ul>
<li v-for="user in users">
{{user['name']}}
</li>
</ul>
<button v-on:click="handleClick">click me</button>
</div>
<script type="text/javascript">
new Vue({
el:'#app',
data : {
message:'hello vue.js',
text : 'this is text string',
html : '<div>hello html</div>',
show :false,
is_show:false,
users:[
{
id:1,
name:'zhangsan'
},
{
id:2,
name:'lisi'
},
{
id:3,
name:'wangwu'
}
]
},
methods:{
handleClick() {
console.log('click response');
}
}
});
</script>
</body>
</html>
我们给button
添加了v-on:click
指令,值为处理函数的名称,当我们点击按钮后,handleClick
会被正常调用。
v-bind
依据官方文档的说法,这个指令是用响应式的更新HTML属性。感觉这种说法还是比较抽象,我们来考虑一个场景,我们要显示一张图片,需要通过img的src属性
,但是如果这张图片的url是通过与服务端交互获取的,那么这是就需要用到v-bind
指令了。
<!DOCTYPE html>
<html>
<head>
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>
<body>
<div id="app">
{{message}}
<p v-text="text"></p>
<p>{{text}}</p>
<div>{{html}}</div>
<div v-html="html"></div>
<div v-show="show">invisible</div>
<div v-if="is_show">testtest</div>
<ul>
<li v-for="user in users">
{{user['name']}}
</li>
</ul>
<button v-on:click="handleClick">click me</button>
<img v-bind:src="imgUrl">
</div>
<script type="text/javascript">
new Vue({
el:'#app',
data : {
message:'hello vue.js',
text : 'this is text string',
html : '<div>hello html</div>',
show :false,
is_show:false,
users:[
{
id:1,
name:'zhangsan'
},
{
id:2,
name:'lisi'
},
{
id:3,
name:'wangwu'
}
],
imgUrl:'https://www.baidu.com/img/bd_logo1.png'
},
methods:{
handleClick() {
console.log('click response');
}
}
});
</script>
</body>
</html>
我们假设imgUrl
是与服务端交互后,动态获取来的,当imgUrl
被赋值以后,图片内容将会自动显示。
v-model
这个标签是实现双向绑定和核心,需要我们深入掌握用法。
<!DOCTYPE html>
<html>
<head>
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>
<body>
<div id="app">
{{message}}
<p v-text="text"></p>
<p>{{text}}</p>
<div>{{html}}</div>
<div v-html="html"></div>
<div v-show="show">invisible</div>
<div v-if="is_show">testtest</div>
<ul>
<li v-for="user in users">
{{user['name']}}
</li>
</ul>
<button v-on:click="handleClick">click me</button>
<img v-bind:src="imgUrl">
<div>
<input type="text" v-model="content">
<div v-text="content"></div>
</div>
</div>
<script type="text/javascript">
new Vue({
el:'#app',
data : {
message:'hello vue.js',
text : 'this is text string',
html : '<div>hello html</div>',
show :false,
is_show:false,
users:[
{
id:1,
name:'zhangsan'
},
{
id:2,
name:'lisi'
},
{
id:3,
name:'wangwu'
}
],
imgUrl:'https://www.baidu.com/img/bd_logo1.png',
content:''
},
methods:{
handleClick() {
console.log('click response');
}
}
});
</script>
</body>
</html>
通过这段代码,我们能直观的感受到v-bind
的作用。view改变以后,会直接改变与该view绑定的数据
。
以上,就介绍完了Vue.js中的常见指令,更多的指令只指令需要我们在学习过程中不断总结。
事件处理
事件处理在指令v-bind
中就已经介绍过了,这里再介绍下v-on
和v-bind
的简写:
<div id="app">
<button v-on:click="handleClick"></button>
等价于=>
<button @click="handleClick"></button>
<img v-bind:src="imgUrl">
等价于=>
<img :src="imgUrl">
</div>
自定义组件
自定义组件的本质,即定义我们自己的标签。<my-component></my-component>
。定义组件分为两步:
- 创建组件
- 注册组件
其中,注册组件又分为全局组件和局部组件。
<!DOCTYPE html>
<html>
<head>
<title></title>
<script src="./vue.js"></script>
</head>
<body>
<div id="app">
{{message}}
<p v-text="text"></p>
<p>{{text}}</p>
<div>{{html}}</div>
<div v-html="html"></div>
<div v-show="show">invisible</div>
<div v-if="is_show">testtest</div>
<ul>
<li v-for="user in users">
{{user['name']}}
</li>
</ul>
<button v-on:click="handleClick">click me</button>
<img v-bind:src="imgUrl">
<div>
<input type="text" v-model="content">
<div v-text="content"></div>
</div>
<global-component></global-component>
<local-component></local-component>
</div>
<div id="vue">
//正常
<global-component></global-component>
//错误
<local-component></local-component>
</div>
<script type="text/javascript">
// local component only global component
var LocalComponent = Vue.extend({
template:'<div>this a custom local component</div>'
});
// step 1 create global component
var GlobalComponent = Vue.extend({
template:'<div>this a custom global component</div>'
});
// step2 register component,first param is component tag name
Vue.component('global-component',GlobalComponent);
new Vue({
el:'#app',
data : {
message:'hello vue.js',
text : 'this is text string',
html : '<div>hello html</div>',
show :false,
is_show:false,
users:[
{
id:1,
name:'zhangsan'
},
{
id:2,
name:'lisi'
},
{
id:3,
name:'wangwu'
}
],
imgUrl:'https://www.baidu.com/img/bd_logo1.png',
content:''
},
methods:{
handleClick() {
console.log('click response');
}
},
components:{
'local-component':LocalComponent
}
});
new Vue({
el:'#vue'
});
</script>
</body>
</html>
vue单文件组件
在实际项目开发中,我们一般不会在每个html中写vue代码,而是在.vue
文件中来写,然后使用webpack
来编译。也就是说,我们在正式的开发中,都是单页面的思想,也就是整个项目中,只有一个html。
.vue文件书写规则
创建CustomComponent.vue
<template>
<div>
</div>
</template>
<script>
export default {
data() {
return {
}
},
methods: {
},
created() {
},
mounted(){
}
}
</script>
<style scoped>
</style>
可以看到,.vue设计的还是非常合理的,<template>,<script>,<style>分成了三个部分,对于开发人员来说,是非常清晰的,在啥地方干啥事。其中需要注意的是scoped
指的是书写的样式只在当前.vue中有效。如果不加scoped
,可能会影响到其他.vue的样式。需要特别注意。
需要特别注意的是,在.vue中,data是函数,而不是对象
。
生命周期
vue实例在创建过程中会经过几个阶段,我们可以在vue生命周期回调函数中实现自己的业务:
- created:vue实例被创建后
- mounted:vue实例挂载了一个html元素
- updated:data发送变化,重新渲染界面
- destroy:vue实例被销毁。
我们一般在created中初始化数据,在mounted中请求数据
。
vue-router
vue-router
是vue生态圈中的一个专注处理路由的项目,在我们开发单页应用中,扮演着重要的角色。它的本质就是,通过不同的url与不同的组件对应
。
在view-router中,大概有这几个概念需要我们加以理解:
- <router-link>和<router-view>
- 动态路由匹配
- 嵌套路由
- 编程式导航
- 命名路由
- 向路由组件传递props
这个时候,我们终于可以回到最开始的时候创建的vue-demo
项目了。我们来分析一下有模板为我们生成的vue-demo
代码结构。
main.js
main.js
是入口文件,从webpack.base.conf.js
里的配置可以确认。
import Vue from 'vue'
import App from './App'
import router from './router'
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
我们看到,这里new Vue({})
就是创建了一个vue实例
,我们之前也是这样干的,不过多了一个router
属性。这个router
属性就是用来配置路由的。我们先来看App.vue
组件,这个组件是根组件
。
删除不重要的代码后:
<template>
<div id="app">
<router-view/>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style>
#app {
}
</style>
我们看到有个<router-view>
标签,直观的说,这个标签就是用来占位的,路由匹配到的组件将显示在这个区域。与<router-view>
相关的还有<router-link>
标签,可以理解成一个a
标签。a
标签配置的url
将会决定占位的区域显示哪一个组件。
我们来看router中的index.js
。
router index.js
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
}
]
})
这里。创建了一个router对象
,配置routes数组
,routes数组
就是用来配置路由和组件的。Vue.use()
是使用插件,我们访问localhost:8080/
就会定位到HelloWorld组件
。
自定义组件和路由 MyComponent.vue
了解了占位以后,我们来自己创建一个组件并配置路由。
<template>
<div>
{{msg}}
</div>
</template>
<script>
export default {
name:'my-component',
data() {
return {
msg: 'hello world'
}
}
}
</script>
<style scoped>
</style>
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import MyComponent from '@/components/MyComponent'
Vue.use(Router)
export default new Router({
mode:'history',
routes: [
{
path:'/mycomponent',
component:MyComponent
},
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
}
]
})
需要注意的是,我们为了方便演示,设置了mode
属性。值为history
。暂时不用了解。
我们通过浏览器访问localhost:8080/mycomponent
,就能正常显示hello world
。
动态路由匹配
我们知道,restapi
风格是目前流行的资源定义形式。例如获得用户信息:
http://localhost:8080/users/10001
我们也可以在vue中配置成如上形式
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import MyComponent from '@/components/MyComponent'
import User from '@/components/User'
Vue.use(Router)
export default new Router({
mode:'history',
routes: [
{
path:'/users/:id',
component:User
},
{
path:'/mycomponent',
component:MyComponent
},
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
}
]
})
变量部分可以用:
打头。
<template>
<div>
当前用户是:{{$route.params.id}}
</div>
</template>
<script>
export default {
created() {
console.log(this.$route.params.id)
}
}
</script>
<style scoped>
</style>
用$route.params.id
可以拿到实际的id值。
假设我们访问localhost:8080/users/10001
,那么id=10001。
嵌套路由
考虑一个场景,我们想创建一个Post
组件作为User
组件的子组件,专门用来呈现该用户的发帖信息,我们就可以使用嵌套路由
。
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import MyComponent from '@/components/MyComponent'
import User from '@/components/User'
import Post from '@/components/Post'
export default new Router({
mode:'history',
routes: [
{
path:'/users/:id',
component:User,
children:[
{
path:'post',
component:Post
}
]
},
{
path:'/mycomponent',
component:MyComponent
},
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
}
]
})
children
属性对应一个数组,也就是说一个父路由可以嵌套多个子路由。
<template>
<div>
<ul>
<li v-for="(item,index) in post" :key="index">
{{item.content}}
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
post: [
{
title:'今天天气真好',
content:'今天天气真好今天天气真好今天天气真好'
},
{
title:'php是最好的语言',
content:'php是最好的语言php是最好的语言php是最好的语言'
}
]
}
},
}
</script>
<style scoped>
</style>
我们访问localhost:8080/users/10001/post
,就可以正常显示:
当前用户是:10001
- 今天天气真好今天天气真好今天天气真好
- php是最好的语言php是最好的语言php是最好的语言
编程式导航
我们注意到,之前有如下两行代码没有讲解:
Vue.use(ViewRouter)
console.log(this.$route.params.id)
use
函数用来安装插件,之后,一些常用的变量和函数就会挂载到vue实之上
,与之类似的还有:
this.$router.push('/');
push()
就是编程式导航,可以用代码控制路由跳转,我们做如下演示:
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import MyComponent from '@/components/MyComponent'
import User from '@/components/User'
import Post from '@/components/Post'
import Other from '@/components/Other'
export default new Router({
mode:'history',
routes: [
{
path:'/other',
component:Other
},
{
path:'/users/:id',
component:User,
children:[
{
path:'post',
component:Post
}
]
},
{
path:'/mycomponent',
component:MyComponent
},
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
}
]
})
<template>
<div class="hello">
{{msg}}
<group>
<cell title="this is a detail message" value="1120"></cell>
</group>
<button @click="handleClick">clicke me</button>
</div>
</template>
<script>
import { Group, Cell,XButton } from 'vux'
export default {
name: 'HelloWorld',
data () {
return {
msg: 'Welcome to Your Vue.js App'
}
},
methods:{
handleClick(){
this.$router.push('/other');
}
},
components:{
[Group.name]:Group,
[Cell.name]:Cell,
[XButton.name]:XButton
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
<template>
<div>
other page
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
</style>
当我们点击按钮之后,不出意外,应该就会正常显示other page
。
命名路由
为了方便,我们可以给路由起一个名字:
{
name:'other',
path:'/other',
component:Other
}
然后可以这样来访问:
this.$router.push({name:'other'});
也是一种推荐的写法。
props
我们之前演示了一种获取动态路由参数的写法,但是比较耦合,必须依赖$route
对象。我们还可以通过props
的方式来获取。
{
props:true,
path:'/users/:id',
component:User,
children:[
{
path:'post',
component:Post
}
]
}
添加一个属性props
,设置为true
。
<template>
<div>
<!-- 当前用户是:{{$route.params.id}} -->
当前用户是:{{id}}
// post
<router-view></router-view>
</div>
</template>
<script>
export default {
props:['id'],
created() {
console.log(this.$route.params.id)
}
}
</script>
添加props
接收。
至此,就全部介绍完了vue相关的开发必备知识。
网友评论