Vue.js 3.0快速入门(附电影购票APP开发实战源码)
文章目录前言一、概述Vue是什么?MVVM是什么?MVVM模式的实现者为什么要使用MVVM二、Here we go!1、第一个Vue程序(基于idea安装vus插件开发)1.1引用vue.js 使用cdn导入1.2创建一个空项目,创建一个文件夹,new一个HTML文件2、熟悉ES 6的语法2.1为什么要使用ES 62.2块作用域构造let和const1、const常量,只能一次赋值2、对象常量3、
前言
文档笔记来源:kuangshenstudy,清华大学出版社,结合视频资源食用更佳,相关资源源码在文末,有需要自取。
一、概述
Vue是什么?
Vue.js是基于JavaScript的一套MVVC的前端框架。集合了众多优秀的主流框架设计思想,轻量、数据驱动(默认单向数据绑定,但也支持双向数据绑定)、学习成本低。 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
MVVM是什么?
MVVM层实现了前后端更好的分离(前端需要的数据只需要请求后端的接口即可)。
MVVM是Model-View-ViewModel的简写。它本质上就是MVC 的改进版。vm指定的是ViewModel,是视图模型。
ViewModel是MVVM模式的核心,是连接View和Model的桥梁。它有两个方向:
- 将模型转化成试图,将后端传递的数据转化成用户看到的界面。
- 将试图转化成模型,即将所看到的页面转化成后端的数据。
在Vue.js框架中这两个方向都实现了,就是Vue.js中数据的双向绑定。
MVVM模式的实现者
Model:模型层, 在这里表示JavaScript对象
View:视图层, 在这里表示DOM(HTML操作的元素)
ViewModel:连接视图和数据的中间件, Vue.js就是MVVM中的View Model层的实现者
为什么要使用MVVM
MVVM模式和MVC模式一样,主要目的是分离视图(View)和模型(Model),有几大好处
低耦合:视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的View上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
可复用:可以把一些视图逻辑放在一个ViewModel里面,让很多View重用这段视图逻辑。
独立开发:开发人员可以专注于业务逻辑和数据的开发(ViewMode),设计人员可以专注于页面设计。
可测试:界面素来是比较难以测试的,而现在测试可以针对ViewModel来写。
二、Here we go!
1、第一个Vue程序(基于idea安装vus插件开发)
1.1引用vue.js 使用cdn导入
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
或者
<!-- 生产环境版本,优化了尺寸和速度 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
不推荐新手直接使用 vue-cli
1.2创建一个空项目,创建一个文件夹,new一个HTML文件
Vue.js 的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统:
(模板块,后期可直接复制模板)
<div id="app">
{{ message }}
</div>
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})
万物始于hello word 创建第一个 vue.js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--view层,模板-->
<div id="app">
<!-- 数据绑定-->
{{message}}
</div>
<!--导入vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script type="text/javascript">
// 创建一个Vue实例
var vm = new Vue({
el:"#app",
/*Model:数据*/
data:{
message:"hello vue~"
}
});
</script>
</body>
</html>
说明:
- el:“#app” -----> 绑定元素的ID
- data:{message:“hello vue~”} ----> 数据对象中有一个名为message的属性,并设置了初始值 hello,vue ~
- {{message}} -----> 实现数据绑定功能
测试:
为了能够更直观的体验Vue带来的数据绑定功能, 我们需要在浏览器测试一番, 操作流程如下:
1、在浏览器上运行第一个Vue应用程序, 进入开发者工具
2、在控制台输入vm.message=‘HelloWorld’, 然后回车, 你会发现浏览器中显示的内容会直接变成HelloWorld,不需要刷新页面
此时就可以在控制台直接输入vm.message来修改值, 中间是可以省略data的, 在这个操作中, 我并没有主动操作DOM, 就让页面的内容发生了变化, 这就是借助了Vue的数据绑定功能实现的; MV VM模式中要求View Model层就是使用观察者模式来实现数据的监听与绑定, 以做到数据与视图的快速响应。
2、熟悉ES 6的语法
2.1为什么要使用ES 6
ES6是一次重大的版本升级,与此同时,由于ES6秉承着最大化兼容已有代码的设计理念,过去编写的 JS 代码还能正常运行。事实上,许多浏览器已经支持部分ES6特性,并继续努力实现其余特性。这意味着,在一些已经实现部分特性的浏览器中,开发者符合标准的 JavaScript 代码已经可以正常运行,可以更加方便地实现很多复杂的操作,提高开发人员的工作效率。
以下是ES6排名前十位的最佳特性列表(排名不分先后)
- Default Parameters (默认参数)
- Template Literals (模板文本)
- Multi - line Strings (多行字符串)
- Destructuring Assignment (解构赋值)
- Enhanced Object Literals (增强的对象文本)
- Arrow Functions (箭头函数)
- Promises
- Block - Scoped Constructs Let and Const (块作用域构造 Let and Const )
- Classes(类)
- Modules(模块)
2.2块作用域构造let和const
块级声明用于声明在指定块的作用域之外无法访问的变量。这里的块级作用域是指函数内部或
者字符{}内的区域。
在ES6中, let 是一种新的变量声明方式。在函数作用域或全局作用域中,通过关键字 var 声明
的变量,无论在哪里声明,都会被当成在当前作用域顶部声明的变量。
function calculateTotalAmount ( vip )(
//只能使用 var 方式定义变量
var amount =0;
if ( vip ){
//在此定义会被覆盖
var amount=1;
//在此定义会被覆盖
var amount=100;
//在此定义会被覆盖
var amount=1000;
return amount;
} //打印内容
console.log(calculateTotal/^mount (true));
以上结果将返回1000,这是一个bug,在ES 6中,用let限制块级作用域,而var限制函数作用域。
funetion calculateTotalAmount(vip){
//使用 var 方式定义变量 var amount =0;
if(vip){
//使用1et定义的局部变量
let amount =1;//第1个 let
let amount =100;//第2个 let
let amount =1000;//第3个 let
}
return amount ;
}
console.log(calculateTotalAmount(true));
程序结果将会是0,因为块作用域中有了 let ,如果 amount=1 ,那么这个表达式将返回1。本例是一个演示,这里有一堆常量,它们互不影响,因为它们属于不同的块级作用域。
JavaScript 中的 var 只能声明一个变量,这个变量可以保存任何数据类型的值。ES6之前并没有定义声明常量的方式,ES6标准中引入了新的关键字 const 来定义常量。
使用 const 定义常量后,常量将无法改变, const 常量的用法说明如下。
1、const常量,只能一次赋值
const PI=3.14159;
PI=3.14;//报错 Assignment to constant variable.
2、对象常量
对象的属性可以修改,对象的引用不能修改
const obj={name:"kerr"};
obj.name="tom";
3、冻结对象
防止修改对象的属性
const obj=Object.freeze({name:"kerr"});
obj.name="tom";//报错,提示冻结对象不能再重新定义赋值
2.3模板字面量
2.3.1 Multi - line Strings (多行字符串)
ES 6的多行字符串是一个实用的功能,在ES 5中我们只能使用以下方法来表示多行字符串:
var roadPoem =, 江南好,风景旧曾谙。'
+,日出江花红胜火,1
+,春来江水绿如蓝。,
+,能不忆江南?'
+'忆江南-江南好1;
然而在ES 6中,仅仅用反引号就可以解决
var roadPoem = `江南好,风景旧曾谙。
日出江花红胜火,
春来江水绿如蓝。
能不忆江南?
2.3.2字符串占位符
使用模板和插入值是在字符串里输出变量的方式,在ES 5中开发者可以组合一个字符串
//在ES 6之前只能使用组合字符串的方式
var name = * your name is * + first + * ' +last + *.*;
var url=* http://localhost:3000/api/messages/* +id;
在ES 6 中,占位符是使用语法${NAME}的,将包含的NAME变量或者表达式放在反引号中
var name=`your name is ${first} ${last}. `;
var url=`http://localhost:3000/api/messages/${id}`;
2.4默认参数和rest参数
JavaScript定义默认参数的方式如下:
//JavaScript原先定义方式
var link=function (height,color,url){
var height =height || 50;
var color=color | | * red*;
var url=url || 'http://baidu.com';
}
在ES 6中,可以直接把默认值放在函数申明中:
var link = function (height=50,color=* red*,url=`http://baidu.com *)
ES 6引入rest参数,用于获取函数的实参,不过rest参数不适合参数个数不确定的函数
ES 5中获取函数的实参:
function data(){
console.log(arguments);
}
data('pg','xj','jz');
在ES 6中,使用rest参数获取函数的实参:
function data(...args){
console.log(args);
}
data('苹果','香蕉','橘子');
rest参数必须放在参数最后的位置
function fn(a,b,...args){
console.log(a);
console.log(b);
console.log(args);
}
fn(100,200,300,400,500,600);
2.5解构赋值
house和mouse是key,同时也是hi变量
var data=$('body') . data();//data拥有两个属性house和mouse
house=data.house;
mouse=data.mouse;
//在Note.js中使用ES 5代码
var j sonMiddleware=require('bady-parser').j sonMiddleware;
var body=req.body;//body的两个属性,username和password
username = body.username;
password=body.password;
在ES 6中可以使用以下代码替换ES 5的代码
var { house,mouse)=$(1 body').data;
var (jsonMiddleware}=require('body-pareser');
var (username,password)=req.body;
//这个也同样适合数组
var [col1,col2]=$(' .cplumn*),[line1z line2,line3z,line5]=file.split(ln');
2.6展开运算符
第一个用途:组装数组
let color = ['red', 'yellow'];
let colorful = [...color, 'green', 'blue'];
console.log(colorful); // ["red", "yellow", "green", "blue"]
第二个用途:获取数组除了某几项的其他项
let num = [1, 3, 5, 7, 9];
let [first, second, ...rest] = num;
console.log(rest); // [5, 7, 9]
2.7增强的对象文本
2.7.1通过变量进行对象初始化
const
a=100,b=200,c=300;
obj={
a
b
c
};
2.7.2简化定义对象方法
const lib={
sum(a,b) { return a+b;},
mult(a,b) {return a*b;}
};
console.log(lib.sum(100,200));//300
console.log(lib.mult(100,200));//20000
这里不能使用ES 6箭头函数(=>),因为该方法需要一个名称。如果直接命名每个方法,则可以使用=>箭头函数。例如:
const lib={
sum:(a,b)=>a+b,
mult:(a,b)=>a*b
};
console.log(lib.sum(100,200));//300
console.log(lib.mult(100,200));//20000
2.7.3动态属性键
通过在方括号[]内置表达式,可以在ES 6中到那个太分配对象键
const
key1='one',
obj={
[key1]:100,
two:200,
three:300
};
//表示obj.one=100,obj.two=200,obj.three=300
2.7.4解构属性中的变量
在ES 6中,通过解构可以创建于等效对象属性同名的变量。
const myObjecct={
one:'洗衣机',
two:'冰箱',
three:'空调'
};
const{one,two,three}=myObject;
//表示 one='洗衣机', two='冰箱', three='空调'
2.8箭头函数
CoffecScript 就是因为有丰富的箭头函数,所以让很多开发者所喜爱。在ES6中,也有丰富的箭头出数。比如,以前我们使用闭包, this 总是预期之外地产生改变,而箭头函数的好处在于,现在 this 可以按照你的预期使用了,身处箭头函数里面, this 还是原来的 this 。
有了箭头函数,我们就不必像使用 that = this 或 self = this 、_ this = this 、 bind ( this )那么麻烦了。例如,下面的代码使用ES5就不是很优雅:
var _this=this ;
$(*.btn *).click ( function (event){
this.ssenData();
})_
在ES6中则不需要使用_ this = this :
$ (*.btn*).click((event)=>{
this.sendData();
})
但并不是完全否定之前的方案,ES6委员会决定,以前的 function 的传递方式也是一个很好的方案,所以它们仍然保留了以前的功能。
下面是另一个例子,通过 call 传递文本给 logUpperCase ()函数,在ES5中:
var logUpperCase = function (){
var this = this ;
this.string = this.string.toUpperCase ();
return function (){
return console.log (_this.string );
}
}
logUpperCase .call({ string:*ES 6 rocks *});
//而在Es6中并不需要用 this 浪费时间
var logUpperCase = function (){
this.string=this.string.toUpperCase ();
return()=>console.log(this.string);
}logUpperCase .call({string: * ES 6 rocks *})();
2.8Promise实现
在ES 6中有标准的Promise实现
下面是使用setTimeout()函数实现异步延迟加载函数;
setTimeout(function){
console.log('yay!*);
},1000);
var wait1000 = new Promise((resolve,reject)=>(
setTimeout(resolve,1000);
}).then(()=>(
console.log(* yay!*);
});
3、熟悉Vue.js的语法
3.1 v-bind
<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<span v-bind:title="message">
鼠标悬停几秒钟查看此处动态绑定的提示信息!
</span>
<!--1.导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script type="text/javascript">
// 创建一个Vue实例
var vm = new Vue({
el:"#app",
/*Model:数据*/
data:{
message:"mest Stduying vue "
}
});
</script>
</div>
</body>
</html>
说明:
- 看到的 v-bind attribute 被称为指令。指令带有前缀 v-,以表示它们是 Vue 提供的特殊 attribute
- 它们会在渲染的 DOM 上应用特殊的响应式行为
3.2 v-if、v-else
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<!--Vue基本语法-->
<body>
<!--view层,模板-->
<div id="app">
<span v-bind:title="message">鼠标悬停几秒钟查看此处动态绑定的提示信息!</span>
<br>
<!--v-if else-->
<h1 v-if="type">yes</h1>
<h2 v-else>No</h2>
<h1 v-if="type==='A'">A</h1>
<h1 v-else-if="type==='B'">B</h1>
<h1 v-else="type==='C'">C</h1>
</div>
<!--导入vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script type="text/javascript">
// 创建一个Vue实例
var vm = new Vue({
el:"#app",
data:{
message:"你好 vue~",
// type:true
type:'A',
counter :0,
message:"mest Stduying vue "
},
methods: {//方法必须定义在Vue的method对象中
sayHi:function (event) {
alert(this.message);
}
},
});
</script>
</body>
</html>
说明:
- 在浏览器上运行,打开控制台
- 在控制台输入vm.type=false然后回车,你会发现浏览器中显示的内容会直接变成NO
注:使用v-*属性绑定数据是不需要双花括号包裹的
3.3 v-for
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<!--Vue基本语法-->
<body>
<!--view层,模板-->
<div id="app">
<!--v-for-->
<li v-for="item in items">
{{item.message}}
</li>
</div>
<!--导入vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script type="text/javascript">
// 创建一个Vue实例
var vm = new Vue({
el:"#app",
data:{
message:"你好 vue~",
// type:true
type:'A',
items: [
{message:'java-vue'},
{message:'前端+后端'},
{message:'java+mysql'}
],
counter :0,
message:"mest Stduying vue "
},
methods: {//方法必须定义在Vue的method对象中
sayHi:function (event) {
alert(this.message);
}
},
});
</script>
</body>
</html>
3.4 v-on
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<!--Vue基本语法-->
<body>
<!--view层,模板-->
<div id="app">
<span v-bind:title="message">鼠标悬停几秒钟查看此处动态绑定的提示信息!</span>
<br>
<!--v-on -->
<button v-on:click="sayHi">click me</button>
<button v-on:click="counter +=1">add</button>
<p>The button above has been clicked {{ counter }} times.</p>
</div>
<!--导入vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script type="text/javascript">
// 创建一个Vue实例
var vm = new Vue({
el:"#app",
data:{
// type:true
type:'A',
counter :0,
message:"mest Stduying vue "
},
methods: {//方法必须定义在Vue的method对象中
sayHi:function (event) {
alert(this.message);
}
},
});
</script>
</body>
</html>
3.5 v-html
该指令用于更新元素的innerHtml。内容按普通html插入。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--view层,模板-->
<div id="app">
<!-- 数据绑定-->
<p v-html="message">古诗欣赏:</p>
</div>
<!--导入vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script type="text/javascript">
// 创建一个Vue实例
var vm = new Vue({
el:"#app",
/*Model:数据*/
data:{
message:'<h3 style="color:red"> 老去惜花心已懒,爱梅犹绕江村。</h3>'
}
});
</script>
</body>
</html>
4、Vue 表单双向绑定
v-model 在内部为不同的输入元素使用不同的 property 并抛出不同的事件:
- text 和 textarea 元素使用 value property 和 input 事件;
- checkbox 和 radio 使用 checked property 和 change 事件;
- select 字段将 value 作为 prop 并将 change 作为事件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<!--双向绑定 v-model-->
<body>
<!--view层,模板-->
<div id="app">
输入的是:<input type="text" v-model="message">{{message}}
<br>
输入的是:<textarea type="text" v-model="message"></textarea>{{message}}
</div>
<!--导入vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script type="text/javascript">
// 创建一个Vue实例
var vm = new Vue({
el:"#app",
data:
{
message:"123"
}
});
</script>
</body>
</html>
说明:
在文本区域插值 ({{text}}) 并不会生效,应用 v-model 来代替
复选框、多选框、下拉框
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--view层,模板-->
<div id="app">
<!--Gender:-->
<input type="radio" name="sex" value="Men" v-model="mest" >Men
<input type="radio" name="sex" value="Female" v-model="mest">Female
<p>choose which:{{mest}}</p>
<br>
<!-- 下拉框:-->
<select v-model="selected" >
<option value="" disabled name="one">--请选择--</option>
<option>A</option>
<!-- <option selected>B</option>-->
<option >B</option>
<option>C</option>
</select>
<p>choose which:{{selected}}</p>
<br>
<!-- 复选框:-->
<input type="checkbox" value="足球" v-model="check">
<label>足球</label>
<input type="checkbox" value="汽车" v-model="check">
<label>汽车</label>
<input type="checkbox" value="音乐" v-model="check">
<label>音乐</label>
<input type="checkbox" value="游戏" v-model="check">
<label>游戏</label>
<p>choose which:{{check}}</p>
</div>
<!--导入vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script type="text/javascript">
// 创建一个Vue实例
var vm = new Vue({
el:"#app",
// data:{
// mest:''
// }
// data:{
// selected:''
// }
data:{
mest:'',
selected:'',
check:['足球']
}
});
</script>
</body>
</html>
组件:
组件是可复用的Vue实例, 说白了就是一组可以重复使用的模板, 跟JSTL的自定义标签、Thymeleal的th:fragment等框架有着异曲同工之妙,通常一个应用会以一棵嵌套的组件树的形式来组织
- Vue.component():注册组件
- cvzhanshi:自定义组件的名字
- template:组件的模板
5、Axios异步通信
- 从浏览器中创建XMLHttpRequests
- 从node.js创建http请求
- 支持Promise API[JS中链式编程]
- 拦截请求和响应
- 转换请求数据和响应数据
- 取消请求
- 自动转换JSON数据
- 客户端支持防御XSRF(跨站请求伪造)
测试Axios
准备data数据
{
"name": "qingjiang",
"url": "https://www.baidu.com/",
"page": 1,
"isNonProfit": true,
"address": {
"street": "含光门",
"city": "陕西西安",
"country": "中国"
},
"links": [
{
"name": "bilibili",
"url": "https://bilibili.com"
},
{
"name": "mest",
"url": "https://www.baidu.com/"
},
{
"name": "百度",
"url": "https://www.baidu.com/"
}
]
}
测试:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
[v-cloak]{
display: none;
}
</style>
</head>
<!--解决闪烁问题v-cloak-->
<div id="vue" v-cloak>
<div>{{info.name}}</div>
<div>{{info.address}}</div>
<a v-bind:href="info.url">click me</a>
</div>
<body>
<!--引入js文件-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script type="text/javascript">
var vm = new Vue({
el:"#vue",
//data()方法 data: 属性
data(){
return{
//请求的返回参数合适,必须和json字符串一致
info:{
name:null,
address:{
street:null,
city:null,
country:null
},
url:null
}
}
},
mounted(){//钩子函数
axios.get('../data.json').then(response=>(this.info=response.data));
}
});
</script>
</body>
</html>
说明:
- 在这里使用了v-bind将a:href的属性值与Vue实例中的数据进行绑定
- 使用axios框架的get方法请求AJAX并自动将数据封装进了Vue实例的数据对象中
- 我们在data中的数据结构必须和Ajax响应回来的数据格式匹配
Vue生命周期图
6、Vue 计算属性、内容分发、自定义事件
6.1 计算属性
计算属性的重点突出在属性两个字上(属性是名词),首先它是个属性其次这个属性有计算的能力(计算是动词),这里的计算就是个函数:简单点说,它就是一个能够将计算结果缓存起来的属性(将行为转化成了静态的属性),仅此而已;可以想象为缓存
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--view层,模板-->
<div id="app">
<p>NowTime:{{currentTime1()}}</p>
<p>ComTime:{{currentTime2}}</p>
</div>
<!--导入vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script type="text/javascript">
// 创建一个Vue实例
var vm = new Vue({
el:"#app",
data:{
message:"hello mest"
},
methods:{
currentTime1:function () {
return Date.now();//返回一个时间戳
}
},
// 计算属性:methods和computed方法不能重名
computed:{
currentTime2:function () {
this.message;
return Date.now();
}
}
});
</script>
</body>
</html>
注意:methods和computed里的东西不能重名,重名之后,只会调用methods的方法
说明:
- methods:定义方法, 调用方法使用currentTime1(), 需要带括号
- computed:定义计算属性, 调用属性使用currentTime2,
不需要带括号:this.message是为了能够让currentTime2观察到数据变化而变化 - 如何在方法中的值发生了变化,则缓存就会刷新!可以在控制台使用vm.message=”你好呀",
改变下数据的值,再次测试观察效果!
6.2 内容分发(插槽)
在Vue.js中我们使用元素作为承载分发内容的出口,可以称其为插槽,可以应用在组合组件的场景中。
需求:需要把下面的内容,让标题和内容通过插槽插入内容
<p>标题</p>
<ul>
<li>abcd</li>
<li>abcd</li>
<li>abcd</li>
</ul>
定义一个代办事情的组件
Vue.component('todo',{
template:'<div>\
<div>代办事项</div>\
<ul>\
<li>cvzhanshi study Java</li>\
</ul>\
</div>'
});
将上面的代码留出一个插槽,即slot
Vue.component('todo',{
template:'<div>\
<slot"></slot>\
<ul>\
<slot"></slot>\
</ul>\
</div>'
});
定义一个名为todo-title的待办标题组件 和 todo-items的待办内容组件
Vue.component('todo-title',{
props:['title'],
template:'<div>{{title}}</div>'
});
//这里的index,就是数组的下标,使用for循环遍历的时候,可以循环出来!
Vue.component("todo-items",{
props:["item","index"],
template:"<li>{{index+1}},{{item}}</li>"
});
slot通过name和组件绑定
Vue.component('todo',{ var vm = new Vue({
el:"#vue",
data:{`在这里插入代码片`
todoItems:['test1','test2','test3']
}
});
template:'<div>\
<slot name="todo-title"></slot>\
<ul>\
<slot name="todo-items"></slot>\
</ul>\
</div>'
});
实例化Vue并初始化数据
var vm = new Vue({
el:"#vue",
data:{
todoItems:['test1','test2','test3']
}
});
将数据通过插槽插入预留出来的位置
<todo>
<todo-title slot="todo-title" v-bind:title="title"></todo-title>
<todo-items slot="todo-items" v-for="item in todoItems" :item="item" ></todo-items>
</todo>
说明:
- slot:是绑定组件用的
- :title --> 是v-bind:title的缩写
完整代码:
<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--view层,模板-->
<div id="vue">
<todo>
<todo-title slot="todo-title" v-bind:title="title"></todo-title>
<!--<todo-items slot="todo-items" v-for="{item,index} in todoItems" v-bind:item="item"></todo-items>-->
<!--如下为简写-->
<todo-items slot="todo-items" v-for="item in todoItems" :item="item" ></todo-items>
</todo>
</div>
<!--1.导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script type="text/javascript">
Vue.component('todo',{
template:'<div>\
<slot name="todo-title"></slot>\
<ul>\
<slot name="todo-items"></slot>\
</ul>\
</div>'
});
Vue.component('todo-title',{
props:['title'],
template:'<div>{{title}}</div>'
});
//这里的index,就是数组的下标,使用for循环遍历的时候,可以循环出来!
Vue.component("todo-items",{
props:["item"],
template:"<li>{{item}}</li>"
});
var vm = new Vue({
el:"#vue",
data:{
title:"mest study vue",
todoItems:['test1','test2','test3']
}
});
</script>
</body>
</html>
7、小结
核心:数据驱动,组件化
优点:借鉴了AngularJS的模块化开发和React的虚拟Dom,虚拟Dom就是把Demo操作放到内存中执行;
常用的属性:
v-if
v-else-if
v-else
v-for
v-on绑定事件,简写@
v-model数据双向绑定
v-bind给组件绑定参数,简写:
组件化:
组合组件slot插槽
组件内部绑定事件需要使用到this.$emit(“事件名”,参数);
计算属性的特色,缓存计算数据
三、第一个vue-cli项目
3.1 Vue-cli简介
vue-cli官方提供的一个脚手架,用于快速生成一个vue的项目模板
预先定义好的目录结构及基础代码,就好比咱们在创建Maven项目时可以选择创建一个骨架项目,这个估计项目就是脚手架,我们的开发更加的快速
3.2 环境配置
Node.js
下载地址: http://nodejs.cn/download/ 安装的时候一直下一步直到结束
确认是否安装成功: 在cmd中运行node -v命令,查看是否能够输出版本号 在cmd中运行npm -v命令,查看是否能够输出版本号
安装node.js淘宝镜像加速器(cnpm)
-g 就是全局安装
npm install cnpm -g
或使用如下语句解决npm速度慢的问题,但是每次install都需要 npm install
–registry=https://registry.npm.taobao.org
安装vue-cli
cnpm install vue-cli-g
#测试是否安装成功#查看可以基于哪些模板创建vue应用程序,通常我们选择webpack
vue list
3.3 第一个vue-cli应用程序
创建一个基于webpack模板的vue应用程序
- 打开DOS系统窗口,进入到D盘
D:\Work\IdeaProjects-Vue\
- 创建一个名为myvue项目。
输入vue create mydemo 按下回车,
提示选择配置方式,包括Vue2.x默认配置、Vue3.0默认配置和手动配置,使用方向键选择Vue3.0(第二个),然后只需等待几秒加载。
提示:项目名称不能为大写,否则无法成功创建
- 项目创建成功后,在对应盘符可以看见项目文件夹,就可以启动项目。
使用"cd myvue"进入项目文件夹,然后使用脚手架提供的“npm run serve”命令启动。
- 如图,出现对应端口号后,可以去本地浏览器访问端口,localhost:8080即可。
提示:创建或加载过程中可能出现加载失败需要fix的情况,当出现问题时,它会给出提示,我们按照提示来就行。通过 npm audit fix来修复就行。极端情况:重新install,再重复以上操作。
四、webpack使用
WebPack是一款模块加载器兼打包工具, 它能把各种资源, 如JS、JSX、ES 6、SASS、LESS、图片等都作为模块来处理和使用
4.1 使用webpack
安装:
npm install webpack -g
npm install webpack-cli -g
测试安装成功:
webpack -v
webpack-cli -v
配置:
entry:入口文件, 指定Web Pack用哪个文件作为项目的入口
output:输出, 指定WebPack把处理完成的文件放置到指定路径
module:模块, 用于处理各种类型的文件
plugins:插件, 如:热更新、代码重用等
resolve:设置路径指向
watch:监听, 用于设置文件改动后直接打包
4.2 使用webpack
创建项目:
创建一个webpack文件夹,使用idea打开
- 创建一个名为modules的目录,用于放置JS模块等资源文件
- 在modules下创建模块文件hello.js
//暴露一个方法:sayHi
exports.sayHi = function(){
document.write("<div>Hello Webpack</div>");
}
- 在modules下创建一个名为main.js的入口文件main.js,用于打包时设置entry属性
//require 导入一个模块,就可以调用这个模块中的方法了
var hello = require("./hello");
hello.sayHi();
- 在项目目录下创建webpack.config.js配置文件,使用webpack命令打包
module.exports = {
entry:"./modules/main.js",
output:{
filename:"./js/bundle.js"
}
}
- 打包:
说明:打包如果失败,就用管理员权限运行webpack
在项目目录下创建HTML页面,如index.html,导入webpack打包后的JS文件
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>狂神说Java</title>
</head>
<body>
<script src="dist/js/bundle.js"></script>
</body>
</html>
直接运行index.html
提示:
参数–watch 用于监听变化,如果要打包的东西有变化,就重新打包
webpack --watch
五、vue-router路由
5.1 安装
基于第一个vue-cli进行测试学习; 先查看node modules中是否存在vue-router, vue-router是一个插件包, 所以我们还是需要用n pm/cn pm来进行安装的
npm install vue-router --save-dev
如果在一个模块化工程中使用它,必须要通过Vue.use()明确地安装路由功能
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter);
5.2 测试路由
- 删除第一个vue-cli项目中的没用的东西
- components 目录下存放我们自己编写的组件
- 定义几个自己的组件 Content.vue 、Main.vue、mest.vue
Content.vue
<template>
<div>
<h1>内容页</h1>
</div>
</template>
<script>
export default {
name:"Content"
}
</script>
Main.vue
<template>
<div>
<h1>首页</h1>
</div>
</template>
<script>
export default {
name:"Main"
}
</script>
Mest.vue
<template>
<div>
<h1>Mest</h1>
</div>
</template>
<script>
export default {
name:"Mest"
}
</script>
- 安装路由,在src目录下,新建一个文件夹:router,专门存放路由,配置路由index.js
import Vue from'vue';
//导入路由插件
import Router from 'vue-router';
//导入上面定义的组件
import Content from '../components/Content';
import Main from '../components/Main';
import Mest from "../components/Mest";
//安装路由
// Vue.use(Router) ;
createApp(App).use(router).mount('#app')
//配置路由
export default new Router({
routes:[
{
//路由路径
path:'/content',
//路由名称
name:'content',
//跳转到组件
component:Content
},{
//路由路径
path:'/main',
//路由名称
name:'main',
//跳转到组件
component:Main
}
,{
//路由路径
path:'/mest',
//路由名称
name:'main',
//跳转到组件
component:Mest
}
]
});
- 在main.js中配置路由
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/>'
})
- 在App.vue中使用路由
<template>
<div id="app">
<!--
router-link:默认会被渲染成一个<a>标签,to属性为指定链接
router-view:用于渲染路由匹配到的组件
-->
<h1>cVzhanshi</h1>
<router-link to="/main">首页</router-link>
<router-link to="/content">内容</router-link>
<router-link to="/mest">mest</router-link>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App',
}
</script>
- 运行npm run dev,然后浏览器访问localhost:8080
六、电影购票APP开发实战
6.1脚手架搭建
- 选择好项目存放的目录,使用Vue脚手架创建一个项目,项目名称为:buyfilm。
vue create buyfilm
按照图示选择功能,选择路由器使用history模式
6.2系统构架
本项目使用的都是本地静态资源,主要是前端展示。
其中public文件夹用来存放项目的静态文件。
在src中,放置了所有的源码文件。components用来放置比较小的的、公用的组件。
views用来放置三个主页面文件。
routers用来放置路由,其中index.js文件是主路由。
main.js是项目入口文件的JavaScript逻辑,在webpack打包之后将被注入到index.html页面中。
6.3项目运行效果:
通过运行 :npm run serve 后会展示出本地网址,打开链接就能访问本购票系统。
App主页面展示效果:
6.4设计项目组件
6.4.1设计头部和底部导航组件
1、头部组件(Header)
<template>
<header id="header">
<h1>{{title}}</h1>
</header>
</template>
<script>
export default {
name: "Header",
// props是子组件访问父组件数据的唯一接口
props:{
title:{
type:String,
default:'风云电影'
}
}
}
</script>
<!--scoped属性实现了私有化的样式-->
<style scoped>
#header{
width: 100%;
height: 50px;
color: #ffffff;
background: #e54847;
border-bottom:1px solid #e54847;
position: relative;
}
#header h1{
font-size: 18px;
text-align: center;
line-height: 50px;
font-weight: normal;
}
#header i{
position: absolute;
left: 5px;top: 50%;
margin-top: -13px;
font-size: 26px;
}
</style>
2、底部导航组件(TabBar)
<template>
<div id="footer">
<!--router-link>组件支持用户在具有路由功能的应用中单击导航。通过to属性指定目标地址,默认渲染为带有正确连接的<a>标签,可以通过配置tag属性生成别的标签。另外,当目标路由成功激活时,链接元素自动设置一个表示激活的css类名-->
<ul>
<router-link tag="li" to="/movie">
<i class="fa fa-film"></i>
<p>电影</p>
</router-link>
<router-link tag="li" to="/cinema">
<i class="fa fa-youtube-square"></i>
<p>影院</p>
</router-link>
<router-link tag="li" to="/mine">
<i class="fa fa-user-circle"></i>
<p>我的</p>
</router-link>
</ul>
</div>
</template>
<script>
export default {
name: "Tabbar"
}
</script>
<style scoped>
#footer{
width: 100%;
height: 50px;
background: white;
border-top: 2px solid #ebe8e3;
position: fixed;
left: 0;
bottom: 0;
}
#footer ul{
display: flex;
text-align: center;
height: 50px;
align-items: center;
}
#footer ul li{
flex: 1;
height: 40px;
}
#footer li.active{color: #f03d37;}
/* router-link-active:路由中自带的样式 选中时的颜色*/
#footer li.router-link-active{color: #f03d37;}
#footer ul i{font-size: 20px;}
#footer ul p{
font-size: 12px;
line-height: 18px;
}
</style>
6.4.2设计电影页面组件
1、城市组件(City)
<template>
<div class="city_body">
<div class="city_list">
<div class="city_hot">
<h2>热门城市</h2>
<ul class="clearfix">
<li>北京</li>
<li>上海</li>
<li>天津</li>
<li>合肥</li>
<li>郑州</li>
</ul>
</div>
<div class="city_sort">
<div>
<h2>A</h2>
<ul>
<li>阿克苏</li>
<li>安康</li>
<li>安庆</li>
</ul>
</div>
<div>
<h2>B</h2>
<ul>
<li>白山</li>
<li>白城</li>
<li>宝鸡</li>
</ul>
</div>
<div>
<h2>C</h2>
<ul>
<li>沧州</li>
<li>长春</li>
<li>昌吉</li>
</ul>
</div>
<div>
<h2>D</h2>
<ul>
<li>大理</li>
<li>大连</li>
<li>大庆</li>
</ul>
</div>
<div>
<h2>E</h2>
<ul>
<li>鄂尔多斯</li>
<li>恩施</li>
<li>鄂州</li>
</ul>
</div>
</div>
</div>
<div class="city_index">
<ul>
<li>A</li>
<li>B</li>
<li>C</li>
<li>D</li>
<li>E</li>
</ul>
</div>
</div>
</template>
<script>
export default {
name: "City"
}
</script>
<style scoped>
#content .city_body{
margin-top: 45px;
display: flex;
width: 100%;
position: absolute;
top: 0;
bottom: 0;
}
.city_body .city_list{
flex: 1;
overflow: auto;
background: #fff5f0;
}
.city_body .city_list::-webkit-scrollbar{
background-color: transparent;
width: 0;
}
.city_body .city_hot{
margin-top: 20px;
}
.city_body .city_hot h2{
padding-left: 15px;
line-height: 30px;
font-size: 14px;
background: #f0f0f0;
font-weight:normal;
}
.city_body .city_hot ul li{
float: left;
background: #fff;
width: 29%;
height: 33px;
margin-top: 15px;
margin-left: 3%;
padding:0 4px;
border: 1px solid #e6e6e6;
border-radius: 3px;
line-height: 33px;
text-align: center;
box-sizing: border-box;
}
.city_body .city_sort div{
margin-top: 20px;
}
.city_body .city_sort h2{
padding-left: 15px;
line-height: 30px;
font-size: 14px;
background: #f0f0f0;
font-weight: normal;
}
.city_body .city_sort ul{
padding-left: 10px;
margin-top: 10px;
}
.city_body .city_sort ul li{
line-height: 30px;
}
.city_body .city_index{
width: 20px;
display: flex;
flex-direction: column;
justify-content: center;
text-align: center;
border-left:1px solid #e6e6e6;
}
</style>
2、正在热映(NowPlaying)
<template>
<div class="movie_body">
<ul>
<li>
<div class="pic_show"><img src="../../../public/images/001.png" alt=""></div>
<div class="info_list">
<h2>机械师2:复活</h2>
<p>观众评<span class="grade"> 8.9</span></p>
<p>主演: 杰森·斯坦森 杰西卡·阿尔芭 汤米·李·琼斯 杨紫琼 山姆·哈兹尔丁</p>
<p>今天50家影院放映800场</p>
</div>
<div class="btn_mall">
购票
</div>
</li>
<li>
<div class="pic_show"><img src="../../../public/images/002.png" alt=""></div>
<div class="info_list">
<h2>敢死队</h2>
<p>观众评<span class="grade"> 8.7</span></p>
<p>主演: 西尔维斯特·史泰龙,杰森·斯坦森,梅尔·吉布森</p>
<p>今天50家影院放映750场</p>
</div>
<div class="btn_mall">
购票
</div>
</li>
<li>
<div class="pic_show"><img src="../../../public/images/003.png" alt=""></div>
<div class="info_list">
<h2>最后的巫师猎人</h2>
<p>观众评<span class="grade"> 8.4</span></p>
<p>主演: 范·迪塞尔,萝斯·莱斯利,伊利亚·伍德,迈克尔·凯恩,丽纳·欧文</p>
<p>今天50家影院放映600场</p>
</div>
<div class="btn_mall">
购票
</div>
</li>
<li>
<div class="pic_show"><img src="../../../public/images/004.png" alt=""></div>
<div class="info_list">
<h2>饥饿游戏3</h2>
<p>观众评<span class="grade"> 7.6</span></p>
<p>主演: 詹妮弗·劳伦斯,乔什·哈切森,利亚姆·海姆斯沃斯</p>
<p>今天50家影院放映550场</p>
</div>
<div class="btn_mall">
购票
</div>
</li>
<li>
<div class="pic_show"><img src="../../../public/images/005.png" alt=""></div>
<div class="info_list">
<h2>钢铁骑士</h2>
<p>观众评<span class="grade"> 7.3</span></p>
<p>主演: 本·温切尔,乔什·布雷纳,玛丽亚·贝罗, 迈克·道尔, 安迪·加西亚</p>
<p>今天50家影院放映500场</p>
</div>
<div class="btn_mall">
购票
</div>
</li>
<li>
<div class="pic_show"><img src="../../../public/images/006.png" alt=""></div>
<div class="info_list">
<h2>奔跑者
</h2>
<p>观众评<span class="grade"> 6.6</span></p>
<p>主演: 尼古拉斯·凯奇,康妮·尼尔森,莎拉·保罗森,彼得·方达</p>
<p>今天50家影院放映500场</p>
</div>
<div class="btn_mall">
购票
</div>
</li>
</ul>
</div>
</template>
<script>
export default {
name: "NowPlaying"
}
</script>
<style scoped>
#content .movie_body{
flex: 1;overflow: auto;
}
.movie_body ul{
margin: 0 12px;
overflow: hidden;
}
.movie_body ul li{margin-top: 12px;display: flex;align-items: center;border-bottom: 1px solid #e6e6e6;padding-bottom: 10px;}
.movie_body .pic_show{width: 64px;height: 90px;}
.movie_body .pic_show img{width: 100%;}
.movie_body .info_list{margin-left:10px;flex: 1;position: relative; }
.movie_body .info_list h2{
font-size: 17px; line-height: 24px;
width: 150px;overflow: hidden;
white-space: nowrap;
text-overflow:ellipsis ;
}
.movie_body .info_list p{
font-size:13px;
color: #666;
line-height: 22px;
width: 200px;
overflow: hidden;
white-space: nowrap;
text-overflow:ellipsis ;
}
.movie_body .info_list .grade{
font-weight: 700;
color: #faaf00;
font-size: 15px;
}
.movie_body .info_list img{
width: 50px;
position: absolute;
right: 10px;
top: 5px;
}
.movie_body .btn_mall, .movie_body .btn_pre{
width: 47px;
height: 27px;
line-height: 28px;
text-align: center;
background-color: #f03d37;
color: #fff;
border-radius: 4px;
font-size: 12px;
cursor: pointer;
}
.movie_body .btn_pre{
background-color: #3c9fe6;
}
</style>
3、即将上映(ComingSoon)
<template>
<div class="movie_body">
<ul>
<li>
<div class="pic_show"><img src="../../../public/images/007.png" alt=""></div>
<div class="info_list">
<h2>佐罗和麦克斯</h2>
<p><span class="person">46465</span>人想看</p>
<p>主演:格兰特·鲍尔 艾米·斯马特 博伊德·肯斯特纳</p>
<p>未来30天内上映</p>
</div>
<div class="btn_pre">
预售
</div>
</li>
<li>
<div class="pic_show"><img src="../../../public/images/008.png" alt=""></div>
<div class="info_list">
<h2>废材特工</h2>
<p><span class="person">64645</span>人想看</p>
<p>主演: 杰西·艾森伯格,克里斯汀·斯图尔特,约翰·雷吉扎莫</p>
<p>未来30天内上映</p>
</div>
<div class="btn_pre">
预售
</div>
</li>
<li>
<div class="pic_show"><img src="../../../public/images/009.png" alt=""></div>
<div class="info_list">
<h2>凤凰城遗忘录</h2>
<p><span class="person">42465</span>人想看</p>
<p>主演:Clint Jordan</p>
<p>未来30天内上映</p>
</div>
<div class="btn_pre">
预售
</div>
</li>
<li>
<div class="pic_show"><img src="../../../public/images/010.png" alt=""></div>
<div class="info_list">
<h2>新灰姑娘</h2>
<p><span class="person">46465</span>人想看</p>
<p>主演: Cassandra Morris,Kristen Day</p>
<p>未来30天内上映</p>
</div>
<div class="btn_pre">
预售
</div>
</li>
<li>
<div class="pic_show"><img src="../../../public/images/011.png" alt=""></div>
<div class="info_list">
<h2>鲨卷风4:四度觉醒</h2>
<p><span class="person">38465</span>人想看</p>
<p>主演: 塔拉·雷德,Ian Ziering,Masiela Lusha</p>
<p>未来30天内上映</p>
</div>
<div class="btn_pre">
预售
</div>
</li>
<li>
<div class="pic_show"><img src="../../../public/images/012.png" alt=""></div>
<div class="info_list">
<h2>全境警戒</h2>
<p><span class="person">46465</span>人想看</p>
<p>主演: 戴夫·巴蒂斯塔,布兰特妮·斯诺,Angelic Zambrana</p>
<p>未来30天内上映</p>
</div>
<div class="btn_pre">
预售
</div>
</li>
</ul>
</div>
</template>
<script>
export default {
name: "ComingSoon"
}
</script>
<style scoped>
#content .movie_body{
flex: 1;overflow: auto;
}
.movie_body ul{
margin: 0 12px;
overflow: hidden;
}
.movie_body ul li{margin-top: 12px;display: flex;align-items: center;border-bottom: 1px solid #e6e6e6;padding-bottom: 10px;}
.movie_body .pic_show{width: 64px;height: 90px;}
.movie_body .pic_show img{width: 100%;}
.movie_body .info_list{margin-left:10px;flex: 1;position: relative; }
.movie_body .info_list h2{
font-size: 17px; line-height: 24px;
width: 150px;overflow: hidden;
white-space: nowrap;
text-overflow:ellipsis ;
}
.movie_body .info_list p{
font-size:13px;
color: #666;
line-height: 22px;
width: 200px;
overflow: hidden;
white-space: nowrap;
text-overflow:ellipsis ;
}
.movie_body .info_list .grade{
font-weight: 700;
color: #faaf00;
font-size: 15px;
}
.movie_body .info_list img{
width: 50px;
position: absolute;
right: 10px;
top: 5px;
}
.movie_body .btn_mall, .movie_body .btn_pre{
width: 47px;
height: 27px;
line-height: 28px;
text-align: center;
background-color: #f03d37;
color: #fff;
border-radius: 4px;
font-size: 12px;
cursor: pointer;
}
.movie_body .btn_pre{
background-color: #3c9fe6;
}
</style>
4、搜索组件(Search)
<template>
<div class="search_body">
<div class="search_input">
<div class="search_input_wrapper">
<i class="fa fa-search"></i>
<input type="text">
</div>
</div>
<div class="search_result">
<h3>电影/电视剧/综艺</h3>
<ul>
<li>
<div class="img"><img src="../../../public/images/001.png" alt=""></div>
<div class="info">
<p><span>机械师2 </span><span>8.9</span></p>
<p>剧情,喜剧,犯罪</p>
<p>2020-6-30</p>
</div>
</li>
</ul>
</div>
</div>
</template>
<script>
export default {
name: "Search"
}
</script>
<style scoped>
#content .search_body{
flex: 1;
overflow: auto;
}
.search_body .search_input{
padding: 8px 10px;
background-color: #f5f5f5;
border-bottom: 1px solid #e5e5e5;
}
.search_body .search_input_wrapper{
padding: 0 10px;
border: 1px solid #e6e6e6;
border-radius: 5px;
background-color: #fff;
display: flex;
}
.search_body .search_input_wrapper i{
font-size: 16px;
padding: 4px 0;
}
.search_body .search_input_wrapper input{
border: none;
font-size: 13px;
color: #333;
padding: 4px 0;
outline: none;
}
.search_body .search_result h3{
font-size: 15px;
color: #999;
padding: 9px 15px;
border-bottom: 1px solid #e6e6e6;
}
.search_body .search_result li{
border-bottom: 1px #c9c9c9 dashed;
padding: 10px 15px;
box-sizing: border-box;
display: flex;
}
.search_body .search_result .img{
width: 60px;
float: left;
}
.search_body .search_result .img img{
width: 100%;
}
.search_body .search_result .info{
float: left;
margin-left: 15px;
flex: 1;
}
.search_body .search_result .info p{
height: 22px;
display: flex;
line-height: 22px;
font-size: 12px;
}
.search_body .search_result .info p:nth-of-type(1) span:nth-of-type(1){
font-size: 18px;
flex: 1;
}
.search_body .search_result .info p:nth-of-type(1) span:nth-of-type(2){
font-size: 16px;
color: #fc7103;
}
</style>
6.4.3设计影院页面组件
影院列表组件(CiList)
<template>
<div class="cinema_body">
<ul>
<li>
<div>
<span>大地影院延庆金锣湾店</span>
<span class="q"><span class="price"> 38.5</span> 元起</span>
</div>
<div class="address">
<span>延庆区北街39号H座首层</span>
<span> >100km </span>
</div>
<div class="card">
<div>小吃</div>
<div>折扣卡</div>
</div>
</li>
<li>
<div>
<span>燕山影剧院</span>
<span class="q"><span class="price"> 37.5</span> 元起</span>
</div>
<div class="address">
<span>房山区燕山岗南路3号</span>
<span> >120km</span>
</div>
<div class="card">
<div>小吃</div>
<div>折扣卡</div>
</div>
</li>
<li>
<div>
<span>万达影城昌平保利光魔店</span>
<span class="q"><span class="price"> 37.9</span> 元起</span>
</div>
<div class="address">
<span>昌平区鼓楼南街佳莲时代广场四层</span>
<span> >80km </span>
</div>
<div class="card">
<div>小吃</div>
<div>折扣卡</div>
</div>
</li>
<li>
<div>
<span>门头沟影剧院</span>
<span class="q"><span class="price"> 30.9</span> 元起</span>
</div>
<div class="address">
<span>门头沟区新桥大街12号</span>
<span> >110km </span>
</div>
<div class="card">
<div>小吃</div>
<div>折扣卡</div>
</div>
</li>
</ul>
</div>
</template>
<script>
export default {
name: "CiList"
}
</script>
<style scoped>
#content .cinema_body{
flex: 1;
overflow: auto;
}
.cinema_body ul{
padding: 20px;
}
.cinema_body li{
border-bottom: 1px solid #e6e6e6;
margin-bottom: 20px;
}
.cinema_body div{
margin-bottom: 10px;
}
.cinema_body .q{
font-size: 11px;
color: #f03d37;
}
.cinema_body .price{
font-size: 18px;
}
.cinema_body .address{
font-size: 13px;
color:#666;
}
.cinema_body .address span:nth-of-type(2){
float: right;
}
.cinema_body .card{
display: flex;
}
.cinema_body .card div{
padding: 0 3px;
height: 15px;
line-height: 15px;
border-radius:2px;
color: #f90;
border:1px solid #f90;
}
.cinema_body .card div.or{
color: #f90;
border: 1px solid #f90;
}
.cinema_body .card div.bl{
color: #589daf;
border: 1px solid #589daf;
}
</style>
6.4.4设计我的页面组件
只有一个登录/注册组件(未实现后端交互)
<template>
<div class="login_body">
<div>
<input class="login_text" type="text" placeholder="账号/手机号/邮箱">
</div>
<div>
<input class="login_text" type="password" placeholder="请输入您的密码">
</div>
<div class="login_btn">
<input type="submit" value="登录">
</div>
<div class="login_link">
<a href="#">立即注册</a>
<a href="#">找回密码</a>
</div>
</div>
</template>
<script>
export default {
name: "Login"
}
</script>
<style scoped>
#content .login_body{
width: 100%;
}
.login_body .login_text{
width: 100%;
height: 40px;
border: none;
border-bottom: 1px #ccc solid;
margin:0 5px;
outline: none;
}
.login_body .login_btn{
height: 50px;
margin: 10px;
}
.login_body .login_btn input{
display: block;
width: 100%;
height: 100%;
background: #e54847;
border-radius: 3px;
border: none;
color: white;
}
.login_body .login_link{
display: flex;
justify-content: space-between;
}
.login_body .login_link a{
text-decoration: none;
margin: 0 5px;
font-size: 12px;
color:#e54847;
}
</style>
6.5设计项目页面组件及路由配置
6.5.1电影页面组件及路由
<template>
<div id="main">
<!-- 头部组件-->
<Header title="风云电影"></Header>
<div id="content">
<div class="movie_menu">
<router-link tag="div" to="/movie/city" class="city_name">
<span>北京 </span><i class="fa fa-caret-down"></i>
</router-link>
<div class="hot_swtich">
<router-link tag="div" to="/movie/nowPlaying" class="hot_item active">正在热映</router-link>
<router-link tag="div" to="/movie/comingSoon" class="hot_item">即将上映</router-link>
</div>
<router-link tag="div" to="/movie/search" class="search_entry">
<i class="fa fa-search"></i>
</router-link>
</div>
<!--二级路由渲染-->
<keep-alive>
<router-view></router-view>
</keep-alive>
</div>
<!-- 尾部组件-->
<TabBar></TabBar>
</div>
</template>
<script>
import Header from '../../components/Header';
import TabBar from '../../components/TabBar';
export default {
name:'Movie',
components:{
Header,
TabBar
}
}
</script>
<style scoped>
#content .movie_menu{
width: 100%;
height: 45px;
border-bottom: 1px solid #e6e6e6;
display: flex;
justify-content: space-between;
}
.movie_menu .city_name{
margin-left: 20px;
height: 100%;
line-height: 45px;
}
.movie_menu .city_name.router-link-active{
color: #ef4238;
border-bottom: 2px solid #ef4238;
box-sizing: border-box;
}
.movie_menu .hot_swtich{
display: flex;
height: 100%;
line-height: 45px;
}
.movie_menu .hot_item{
font-size: 15px;
color: #666;
width: 80px;
text-align: center;
margin: 0 12px;
font-weight: 700;
}
.movie_menu .hot_item.router-link-active{
color: #ef4238;
border-bottom:2px solid #ef4238;
}
.movie_menu .search_entry{
margin-right: 20px;
height: 100%;
line-height: 45px;
}
.movie_menu .search_entry.router-link-active{
color: #ef4238;
border-bottom:2px solid #ef4238;
box-sizing: border-box;
}
.movie_menu .search_entry i{
font-size: 24px;
color: red;
}
</style>
配置路由:
// movie路由
export default {
path:'/movie',
//按需载入的方式
component:()=>import('../../views/Movie'),
// 二级路由,使用children进行配置
children:[
{
path:'city',
component:()=>import('../../components/City')
},
{
path:'nowPlaying',
component:()=>import('../../components/NowPlaying')
},
{
path:'comingSoon',
component:()=>import('../../components/ComingSoon')
},
{
path:'search',
component:()=>import('../../components/Search')
},
// 重定向:当路径为/movie时,重定向到/movie/nowPlaying路径
{
path:'/movie',
redirect:'/movie/nowPlaying'
}
]
}
6.5.2影院页面组件及路由
<template>
<div id="main">
<Header title="风云影院"></Header>
<div id="content">
<div class="cinema_menu">
<div class="city_switch">
全城 <i class="fa fa-caret-down"></i>
</div>
<div class="city_switch">
品牌 <i class="fa fa-caret-down"></i>
</div>
<div class="city_switch">
特色 <i class="fa fa-caret-down"></i>
</div>
</div>
<CiList></CiList>
</div>
<TabBar></TabBar>
</div>
</template>
<script>
import Header from '../../components/Header';
import TabBar from '../../components/TabBar';
import CiList from '../../components/CiList';
export default {
name:'Cinema',
components:{
Header,
TabBar,
CiList
}
}
</script>
<style scoped>
#content .cinema_menu{
width: 100%;
height: 45px;
border-bottom: 1px solid #e6e6e6;
display: flex;
justify-content: space-around;
align-items: center;
background: white;
}
</style>
配置路由:
// Cinema路由
export default {
path:'/cinema',
component:()=>import('../../views/Cinema')
}
6.5.3我的页面组件及路由
<template>
<div id="main">
<Header title="我的影院"></Header>
<div id="content">
<Login></Login>
</div>
<TabBar></TabBar>
</div>
</template>
<script>
import Header from '../../components/Header';
import TabBar from '../../components/TabBar';
import Login from '../../components/Login';
export default {
name:'Mine',
components:{
Header,
TabBar,
Login
}
}
</script>
<style scoped></style>
配置路由:
// mine路由
export default {
path:'/mine',
component:()=>import('../../views/Mine')
}
总结
百度文库源码提取链接:https://pan.baidu.com/s/1k_65SO6rIQxDaTe-So3lpw
提取码:点赞文章后私聊
参考文献:《Vue.js 3.0从入门到精通》清华大学出版社
文档参考:https://blog.csdn.net/qq_45408390/article/details/118151297
Vue中文文档:https://cn.vuejs.org/v2/guide/
更多推荐
所有评论(0)