文章目录
1.1 安装Vue-cli
1.1.1 安装Node版本
Node官网:http://nodejs.p2hp.com/download/
1.1.2 安装脚手架vue-cli
安装脚手架vue-cli命令:npm install vue-cli -g
检查脚手架是否安装成功:vue
1.1.3 vue-cli创建项目
- 创建指定名称项目:
vue init webpack vuetestdemo
- ?Project name: 设置项目名称
- ?Project description:设置项目描述
- ?Author:设置作者
- ?Vue build:项目构建方式
- ?Install vue-router:是否需要安装路由
- ?Use ESLint:是否需要安装代码检验ESLint
- ?Setup uint tests:是否安装测试项目
- ?Setup e2e tests:是否安装e2e测试项目
- ?Should we run ‘npm install …’:选择模块安装方式
1.1.4 运行脚手架项目
运行项目:npm run dev
访问路由:http://localhost:8080/#/
1.2 Vue-cli目录结构
项目目录下文件介绍:
build
和config
文件夹分别是Webpack配置目录
和Vue配置目录
(做项目时一般不会修改它们,在项目完成后打包时候,可能会操作这两个目录)node_modules文件夹
:相当于第三方库,通过npm安装的依赖包都会在 node_modulessrc文件夹
:- build/ :Webpack配置目录
- config/ :Vue配置目录
- node_modules/ :第三方库文件夹子
- src/ :主代码文件夹
- assets/:存放静态资源(JS、CSS、images)
- components/:存放公共组件
- router/:存放路由匹配规则
- App.vue:入口组件
- main.js:入口JS文件
- static/ :存放静态文件
- .babelrc:用于解析es6代码
- .editorconfig:定义代码格式(编辑器用的)
- .gitignore:git版本控制配置文件
- .postcssrc.js:CSS转换工具,类似less、sass的预处理器
- index.html:html主页面,用于挂在vue实例
- package.json:记录
npm install
安装模块的版本号、项目名称等 - package-lock.json:记录安装模块的版本号和来源(下载地址)
- README.md:说明文档
区别目录assets 和 static :
- assets 目录会被Webpack处理,图片变为base64格式
- static 目录不会被Webpack处理,直接复制到最终打包的目录
注意:
- 建议把JS、CSS放在static目录下,因为被Webpack压缩处理后,其体积会变小,有利于提升页面的加载速度。
- 第三方静态文件放在static目录下,因为第三方静态文件一般都是被处理过的,无须让Webpack进行压缩处理。
- 图片资源:大图片放在static目录下,小图片放在assets目录下。
1.3 Vue-cli运行机制
1.3.1 index.html文件
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>vuetestdemo</title>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
当运行项目 npm run dev
,首先启动index.html,但是index.html中只有一个div元素,没有其他内容,理论上只会显示空页面。
此时,Webpack在index.html中自动引入了main.js,即当运行 npm run dev
时,会自动在index.html中引入main.js。
1.3.2 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'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
从 new Vue()开始:
- el属性表示控制index.html文件中id为app的元素
- router表示Vue实例中使用路由
- components表示Vue实例中有一个App组件
- template表示要渲染的内容
注意:在Vue生命周期中,当Vue同时出现el属性和template属性,template属性中内容会覆盖掉el属性中的内容,所以最终页面上渲染的内容是template中的内容,也即App组件。
1.3.3 App.vue组件
App.vue组件
<template>
<div id="app">
<img src="./assets/logo.png">
<router-view/>
</div>
</template>
<script>
export default {
name: 'App'
}
</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>
App.vue是一个完整的组件,三部分组成:template、script、style。
- template中只能有一个节点。
- < router-view />中显示的内容是由路由匹配规则规定的。
1.3.4 router下index.js
在main.js中 import router from './router'
引入了路由对象,进入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
}
]
})
代码解析:
- 引入Hello World组件
- 创建路由匹配规则,当路径为 / 时,显示HelloWorld组件,显示的位置就是App组件中 < router-view />
1.4 Vue-cli选项卡案例
在components文件夹下新建组件 01tab.vue
<template>
<div>
<h1>选项卡</h1>
<span :class="{active:isactive == 0}" @click="mybtn(0)">娱乐新闻</span>
<span :class="{active:isactive == 1}" @click="mybtn(1)">体育新闻</span>
<span :class="{active:isactive == 2}" @click="mybtn(2)">八卦新闻</span>
<ul>
<li v-for="(item,i) in newList[isactive]" :key="i">
{{item}}
</li>
</ul>
</div>
</template>
<script>
export default{
data(){
return{
isactive:0,
newList:[
['娱乐新闻1','娱乐新闻2'],
['体育新闻1','体育新闻2'],
['八卦新闻1','八卦新闻2'],
]
}
},
methods:{
mybtn(i){
this.isactive = i;
}
}
}
</script>
<style scoped>
/* scoped属性表示当前样式只在当前页面中显示 */
.active{color:red}
</style>
在 router/index.js中配置路由:
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import tab from '../components/01tab.vue'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
},
{
path: '/tab',
name: 'tab',
component: tab
}
]
})
1.5 私有过滤器
过滤器:用于数据输出之前的处理,例如给数字添加小数点。
过滤器分为全局过滤器和私有过滤器。
1.5.1 私有过滤器
- 过滤器filters 和 data属性、methods属性平级。
- 过滤器是一个方法,其第一个参数是固定的,表示要处理的变量,且必须要有return属性。
- 过滤器使用竖线又称作管道符,竖线后面就是过滤器的名字。
<template>
<div class="hello">
<h1>{{num | toFixed(5,"$")}}</h1>
<h1>{{msg}}</h1>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data () {
return {
num:10,
msg: 'Welcome to Your Vue.js App'
}
},
filters:{
toFixed(val,data,data1){
return data1 + val.toFixed(data)
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1, h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
1.5.2 全局过滤器
全局过滤器是在main.js中定义的,全局过滤器使用Vue.filter方法创建,第一个参数是过滤器的名字,其调用方法和私有过滤器的调用方法相同。
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'
Vue.config.productionTip = false //设置在控制台环境进行代码提示作用
// 全局过滤器
Vue.filter('toFixed1',function(val,data,data1){
return data1 + val.toFixed(data)
})
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
1.6 Vue计算属性
- 计算属性computed 和 data、methods平级。
- 计算属性依赖于普通属性
注意:使用计算属性时,调用不能加()当作函数使用。
<template>
<div class="hello">
<input type="text" placeholder="请输入用户名" v-model="user"/>
<input type="text" placeholder="请输入密码" v-model="password"/>
<button :class="{active:getActive}">登录</button>
<h1>{{getAvg}}</h1>
<h1>{{getSum}}</h1>
<h1>{{num | toFixed(5,"$")}}</h1>
<h1>{{num1 | toFixed1(5,"$")}}</h1>
<h1>{{msg}}</h1>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data () {
return {
num:10,
num1:20,
msg: 'Welcome to Your Vue.js App',
user:'',
password:'',
}
},
filters:{
toFixed(val,data,data1){
return data1 + val.toFixed(data)
}
},
computed:{
getSum(){
return this.num + this.num1;
},
getAvg(){
return this.getSum/2;
},
getActive(){
if(this.user==''||this.password==''){
return false
}
return true
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.active{
color: red;
}
h1, h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
1.7 watch侦听属性
- 侦听属性用于侦听data中数据的变化,只要data中数据发生变化,就会触发watch侦听属性。
- watch侦听属性与computed计算属性是同一等级。
- 一个侦听属性只能侦听一个变量。
- 计算属性computed与侦听属性watch区别
- computed中的计算属性可以随意命名
- watch中的侦听属性要与侦听的data中的变量名一致,如:data中的num3属性,则侦听属性为num3()
- 侦听属性不需要return返回值
<template>
<div class="hello">
<input type="text" placeholder="请输入用户名" v-model="t1"/>
<input type="text" placeholder="请输入密码" v-model="t2"/>
<button :class="{active:isTrue}">登录</button>
<hr/>
<input type="text" name="" id="" v-model="num3"/>
<hr/>
<input type="text" placeholder="请输入用户名" v-model="user"/>
<input type="text" placeholder="请输入密码" v-model="password"/>
<button :class="{active:getActive}">登录</button>
<h1>{{getAvg}}</h1>
<h1>{{getSum}}</h1>
<h1>{{num | toFixed(5,"$")}}</h1>
<h1>{{num1 | toFixed1(5,"$")}}</h1>
<h1>{{msg}}</h1>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data () {
return {
num:10,
num1:20,
num3:100,
msg: 'Welcome to Your Vue.js App',
user:'',
password:'',
isTrue:false,
t1:'',
t2:'',
}
},
filters:{
toFixed(val,data,data1){
return data1 + val.toFixed(data)
}
},
computed:{
getSum(){
return this.num + this.num1;
},
getAvg(){
return this.getSum/2;
},
getActive(){
if(this.user==''||this.password==''){
return false
}
return true
}
},
watch:{
num3(){
console.log("num3修改了")
},
t1(){
if(this.t1 == '' || this.t2 ==''){
this.isTrue = false
}else{
this.isTrue = true
}
},
t2(){
if(this.t1 == '' || this.t2 ==''){
this.isTrue = false
}else{
this.isTrue = true
}
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.active{
color: red;
}
h1, h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
1.8 slot插槽
slot插槽:用于组件之间传值。
- 传统的父组件向子组件传递信息方式:
- 通过使用v-bind属性进行传递,在子组件中使用props属性接受即可。
- 现在的父组件向子组件传递信息使用slot插槽:
- slot插槽传递属性分为:单一属性传递、多数据传递(具名插槽)
1.8.1 单一属性传递
02slot.vue
<template>
<div>
<h1>
Slot插槽
</h1>
<h2>
<slot></slot>
</h2>
<h3>
</h3>
</div>
</template>
<script>
export default{
}
</script>
<style scoped>
</style>
HelloWorld.vue
<template>
<div class="hello">
<myslot>
{{msg}}
</myslot>
<hr/>
<input type="text" placeholder="请输入用户名" v-model="t1"/>
<input type="text" placeholder="请输入密码" v-model="t2"/>
<button :class="{active:isTrue}">登录</button>
<hr/>
<input type="text" name="" id="" v-model="num3"/>
<hr/>
<input type="text" placeholder="请输入用户名" v-model="user"/>
<input type="text" placeholder="请输入密码" v-model="password"/>
<button :class="{active:getActive}">登录</button>
<h1>{{getAvg}}</h1>
<h1>{{getSum}}</h1>
<h1>{{num | toFixed(5,"$")}}</h1>
<h1>{{num1 | toFixed1(5,"$")}}</h1>
<h1>{{msg}}</h1>
</div>
</template>
<script>
import myslot from './02slot'
export default {
name: 'HelloWorld',
data () {
return {
num:10,
num1:20,
num3:100,
msg: 'Welcome to Your Vue.js App',
user:'',
password:'',
isTrue:false,
t1:'',
t2:'',
}
},
filters:{
toFixed(val,data,data1){
return data1 + val.toFixed(data)
}
},
computed:{
getSum(){
return this.num + this.num1;
},
getAvg(){
return this.getSum/2;
},
getActive(){
if(this.user==''||this.password==''){
return false
}
return true
}
},
watch:{
num3(){
console.log("num3修改了")
},
t1(){
if(this.t1 == '' || this.t2 ==''){
this.isTrue = false
}else{
this.isTrue = true
}
},
t2(){
if(this.t1 == '' || this.t2 ==''){
this.isTrue = false
}else{
this.isTrue = true
}
}
},
components:{
myslot,
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.active{
color: red;
}
h1, h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
1.8.2 多数据传递(具名插槽)
02slot.vue
<template>
<div>
<h1>
Slot插槽
</h1>
<h2>
<slot name="name1"></slot>
</h2>
<h3>
<slot name="name2"></slot>
</h3>
</div>
</template>
<script>
export default{
}
</script>
<style scoped>
</style>
HelloWorld.vue
<template>
<div class="hello">
<myslot>
<div slot="name1">
{{msg}}
</div>
<div slot="name2">
{{num}}
</div>
</myslot>
<hr/>
<input type="text" placeholder="请输入用户名" v-model="t1"/>
<input type="text" placeholder="请输入密码" v-model="t2"/>
<button :class="{active:isTrue}">登录</button>
<hr/>
<input type="text" name="" id="" v-model="num3"/>
<hr/>
<input type="text" placeholder="请输入用户名" v-model="user"/>
<input type="text" placeholder="请输入密码" v-model="password"/>
<button :class="{active:getActive}">登录</button>
<h1>{{getAvg}}</h1>
<h1>{{getSum}}</h1>
<h1>{{num | toFixed(5,"$")}}</h1>
<h1>{{num1 | toFixed1(5,"$")}}</h1>
<h1>{{msg}}</h1>
</div>
</template>
<script>
import myslot from './02slot'
export default {
name: 'HelloWorld',
data () {
return {
num:10,
num1:20,
num3:100,
msg: 'Welcome to Your Vue.js App',
user:'',
password:'',
isTrue:false,
t1:'',
t2:'',
}
},
filters:{
toFixed(val,data,data1){
return data1 + val.toFixed(data)
}
},
computed:{
getSum(){
return this.num + this.num1;
},
getAvg(){
return this.getSum/2;
},
getActive(){
if(this.user==''||this.password==''){
return false
}
return true
}
},
watch:{
num3(){
console.log("num3修改了")
},
t1(){
if(this.t1 == '' || this.t2 ==''){
this.isTrue = false
}else{
this.isTrue = true
}
},
t2(){
if(this.t1 == '' || this.t2 ==''){
this.isTrue = false
}else{
this.isTrue = true
}
}
},
components:{
myslot,
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.active{
color: red;
}
h1, h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
1.9 链式路由跳转
- 传统的路由跳转方式
<router-link to="/tab">选项卡</router-link>
- 链式路由跳转方式
HelloWorld.vue
<template>
<div class="hello">
<button @click="mytab">点击</button>
<hr/>
<router-link to="/tab">选项卡</router-link>
<hr/>
<myslot>
<div slot="name1">
{{msg}}
</div>
<div slot="name2">
{{num}}
</div>
</myslot>
<hr/>
<input type="text" placeholder="请输入用户名" v-model="t1"/>
<input type="text" placeholder="请输入密码" v-model="t2"/>
<button :class="{active:isTrue}">登录</button>
<hr/>
<input type="text" name="" id="" v-model="num3"/>
<hr/>
<input type="text" placeholder="请输入用户名" v-model="user"/>
<input type="text" placeholder="请输入密码" v-model="password"/>
<button :class="{active:getActive}">登录</button>
<h1>{{getAvg}}</h1>
<h1>{{getSum}}</h1>
<h1>{{num | toFixed(5,"$")}}</h1>
<h1>{{num1 | toFixed1(5,"$")}}</h1>
<h1>{{msg}}</h1>
</div>
</template>
<script>
import myslot from './02slot'
export default {
name: 'HelloWorld',
data () {
return {
num:10,
num1:20,
num3:100,
msg: 'Welcome to Your Vue.js App',
user:'',
password:'',
isTrue:false,
t1:'',
t2:'',
}
},
filters:{
toFixed(val,data,data1){
return data1 + val.toFixed(data)
}
},
computed:{
getSum(){
return this.num + this.num1;
},
getAvg(){
return this.getSum/2;
},
getActive(){
if(this.user==''||this.password==''){
return false
}
return true
}
},
watch:{
num3(){
console.log("num3修改了")
},
t1(){
if(this.t1 == '' || this.t2 ==''){
this.isTrue = false
}else{
this.isTrue = true
}
},
t2(){
if(this.t1 == '' || this.t2 ==''){
this.isTrue = false
}else{
this.isTrue = true
}
}
},
components:{
myslot,
},
methods:{
mytab(){
//链式路由跳转
this.$router.push({
// 方式一
// name:'tab'
// 方式二
path:'/tab',
query:{
id:100
}
})
}
},
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.active{
color: red;
}
h1, h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
01tab.vue
<template>
<div>
<h1>选项卡=={{$route.query.id}}</h1>
<span :class="{active:isactive == 0}" @click="mybtn(0)">娱乐新闻</span>
<span :class="{active:isactive == 1}" @click="mybtn(1)">体育新闻</span>
<span :class="{active:isactive == 2}" @click="mybtn(2)">八卦新闻</span>
<ul>
<li v-for="(item,i) in newList[isactive]" :key="i">
{{item}}
</li>
</ul>
</div>
</template>
<script>
export default{
data(){
return{
isactive:0,
newList:[
['娱乐新闻1','娱乐新闻2'],
['体育新闻1','体育新闻2'],
['八卦新闻1','八卦新闻2'],
]
}
},
methods:{
mybtn(i){
this.isactive = i;
}
},
filters:{
toFixed(val){
return val
}
}
}
</script>
<style scoped>
/* scoped属性表示当前样式只在当前页面中显示 */
.active{color:red}
</style>
1.10 全局路由守卫
路由守卫就是在进入页面之前做一层判断,如果没有守卫,可以直接进入页面,如果添加了守卫,则需要做页面跳转。
路由守卫分为:全局路由守卫
、组件内路由守卫
、离开组件守卫
。
组件1 —— login.vue
<template>
<div>
<h1>登录组件</h1>
</div>
</template>
<script>
export default{
}
</script>
<style scoped>
</style>
组件2 —— mycar.vue
<template>
<div>
<h1>汽车组件111</h1>
</div>
</template>
<script>
export default{
}
</script>
<style scoped>
</style>
组件3 —— mydetail.vue
<template>
<div>
<h1>详情组件1111</h1>
</div>
</template>
<script>
export default{
}
</script>
<style scoped>
</style>
路由引入组件:router/indedx.js
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import tab from '../components/01tab.vue'
import login from '../components/login.vue'
import mycar from '../components/mycar.vue'
import mydetail from '../components/mydetail.vue'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
},
{
path: '/tab',
name: 'tab',
component: tab
},
{
path: '/login',
name: 'login',
component: login
},
{
path: '/mycar',
name: 'mycar',
component: mycar,
meta:{
needAuth:true
}
},
{
path: '/mydetail',
name: 'mydetail',
component: mydetail
}
]
})
全局路由守卫: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'
Vue.config.productionTip = false //设置在控制台环境进行代码提示作用
// 1.全局路由守卫
router.beforeEach((to,from,next) => {
/*
to:表示要去的新页面
from:表示旧的页面
next:表示是否
*/
// 0——表示未登录
// 1——表示已登录
var islogin = 1;
if(to.meta.needAuth){
if(islogin == 0){
router.push({name:'login'})
}
if(islogin == 1){
next()
}
}else{
next()
}
})
// 2.全局过滤器
Vue.filter('toFixed1',function(val,data,data1){
return data1 + val.toFixed(data)
})
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
声明 islogin 变量,0表示未登录,1表示已登录。
1.11 组件内路由守卫
- 组件内路由守卫是没有router对象
- 组件内路由不需要meta属性
myorder组件——myorder.vue
<template>
<div>
<h1>下单组件1111</h1>
</div>
</template>
<script>
export default{
beforeRouteEnter:(to, from, next) => {
var islogin = 1
if(islogin == 0){
// 组件内路由守卫是没有router对象
next({name:'login'})
}else{
next()
}
}
}
</script>
<style scoped>
</style>
1.12 离开页面时守卫
myorder组件——myorder.vue
<template>
<div>
<h1>下单组件1111</h1>
</div>
</template>
<script>
export default{
// 1、进入组件时守卫
beforeRouteEnter:(to, from, next) => {
var islogin = 1
if(islogin == 0){
// 组件内路由守卫是没有router对象
next({name:'login'});
}else{
next();
}
},
// 2、离开组件时守卫
beforeRouteLeave:(to, from, next) => {
if(confirm('是否离开页面?')){
next();
}
}
};
</script>
<style scoped>
</style>