vue学习记录
近来在做web开发,以vue作为web前端,从0开始一些简单的学习。
下面是一些基本的vue概念和一些学习代码,在学习时逐步记录下来。
VUE
是一个javascript的渐进式的用户界面网页开发框架,便于我们快速开发网站。
竞争对手
Angular - 学习曲线比较陡峭
ReactJS - 代码可读性比较差
EmberJS,Knockout等
一般来说,JQuery算第一代js网页开发技术,Angular算第二代,React,Vue算第三代。
推荐学习站点
http://vuejs-templates.github.io/
最基本支持
cli:vue的命令行方式使用和开发
vue-router: 路由框架 https://router.vuejs.org/
vuex: 状态管理 https://vuex.vuejs.org/
VUE加强套餐
vue-chartjs
vue-fa
- 用来调用fontAwesome5的组件,实现各种小 icon 的实现。
- https://cweili.github.io/vue-fa/
vee-validate
- 基于模板的vue校验框架,用来做一些前端对表单提交的预验证处理
- https://logaretm.github.io/vee-validate/
eslint-plugin-vue
- 用来规范vue文法的校验工具,可以集成到vscode
- https://eslint.vuejs.org/
vue-lazyload
- 用来图片懒装载,这样图片会在加载时显示loading,加载完毕后自动正常显示
- https://github.com/hilongjw/vue-lazyload
axios
- HTTP通讯组件,可以远程访问rest api服务
- https://github.com/axios/axios
vuedraggable
- VUE网页对象拖动组件
- https://github.com/Sortablejs/vue.draggable
- 演示:https://github.com/Sortablejs/vue.draggable
vue-socket.io
- 对socket.io(基于websocket)封装,可以和vuex状态管理配合使用
- https://github.com/metinseylan/vue-socket.io
vue-multiselect
- 多选框,包括状态管理,下拉框,Ajax,检索框等功能
- https://vue-multiselect.js.org/
vuejs-datepicker
vue-element-admin
- Vue的文本输入框,Markdown编辑器,Svg图表,剪切板,Excel导出,复杂组合table表格 (很实用)
- https://panjiachen.github.io/vue-element-admin-site/
vue-typer
- 打印机效果(逐字符慢慢显示)
- https://cngu.github.io/vue-typer/
vue-good-table
- 复杂组合table表格 (很实用), 比vue-element-admin简单小巧一些,但功能也少一些。
- https://xaksis.github.io/vue-good-table/
vue ui/ux 框架
- bootstrap-vue, element, vuetifyjs, vue-strap
- https://bootstrap-vue.org/
- http://element-cn.eleme.io
- https://vuetifyjs.com
- http://yuche.github.io/vue-strap/
nuxtjs
基于vue的通用应用框架
- 支持自动代码分层
- 服务器渲染
- 异步数据
- 静态文件服务
- 压缩打包js和css
- 本地开发热加载
- 集成eslint
- 支持http/2推送
- 支持多种样式处理:sass, less, stylus等
学习代码
1. HelloWorld
- 添加引用
<script src="https://unpkg.com/vue/dist/vue.js"></srcipt>
- 添加html代码块
<div id="myApp">
{{ message }}
</div>
- 添加javascript代码块
var myApp = new Vue({
el: "#myApp", // 对应div id
data: {
message: "Hello, world!" // 此处定义变量,HTML中使用
}
});
- 完整代码
<!DOCTYPE html>
<html>
<head>
<title>freeknight Vue test</title>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
<div id='myApp'>
{{ message }}
</div>
<script>
var myApp = new Vue({
el: '#myApp',
data: {
message: "Hello, world!"
}
});
</script>
</body>
</html>
**流程控制符 **
v-if, v-for
<!DOCTYPE html>
<html>
<head>
<title>freeknight Vue test 2</title>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
<div id='myApp'>
<h3>游戏列表</h3>
<div v-if="isCanBeSeen">可控制隐藏项</div>
<ol>
<li v-for="game in games">{{game.title}} / {{game.price}}元</li>
</ol>
</div>
<script>
var myApp = new Vue({
el: '#myApp',
data: {
isCanBeSeen: true, // 控制中间的div项是否存在。注意,不仅仅是不显示,而是直接不存在,不会加入dom树。
games:[
{title:"游戏名1", price:200},
{title:"游戏名2", price:300},
{title:"游戏名3", price:500},
],
}
});
</script>
</body>
</html>
**用户输入绑定 **
v-model 将用户输入绑定到一个js变量,以供其他地方访问使用。
<!DOCTYPE html>
<html>
<head>
<title>freeknight Vue test 3</title>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
<div id='myApp'>
<input v-model='myGame'></input>
<p>你最喜欢的游戏是: {{myGame}}</p>
</div>
<script>
var myApp = new Vue({
el: '#myApp',
data: {
myGame: "默认游戏名", // 这个变量被v-model绑定。当<input>更变,myGame变量更变,也就导致 <p> 元素数据发生更变。
}
});
</script>
</body>
</html>
按钮事件响应
v-on:{ click,keydown,keyup,dbclick,load …} 就是绑定按钮各种状态的事件处理
<!DOCTYPE html>
<html>
<head>
<title>freeknight Vue test 4</title>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
<div id='myApp'>
<p>你最喜欢的游戏是: {{myGame}}</p>
<button v-on:click="btnClick('游戏1')">游戏1</button>
<button v-on:click="btnClick('游戏2')">游戏2</button>
<!-- 下面的 @ 符号,等同于 v-on: 的简写 -->
<button @click="btnClick('游戏3')">游戏3</button>
</div>
<script>
var myApp = new Vue({
el: '#myApp',
data: {
myGame: "默认游戏名"
},
methods:{
btnClick: function(gameName){
this.myGame = gameName;
}
}
});
</script>
</body>
</html>
组件
component 一个功能的区域块,有一个自定义的html tag标签。
v-bind 进行组件属性绑定
<!DOCTYPE html>
<html>
<head>
<title>freeknight Vue test 5</title>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
<div id='myApp'>
<ol>
<!-- v-bind 进行参数/属性绑定 -->
<game-item v-for="game in games" v-bind:eachgame="game"></game-item>
</ol>
</div>
<script>
// 自定义组件 game-item
Vue.component('game-item',{
props: ['eachgame'], // 组件的一个属性
template: '<li>{{ eachgame.title }}</li>' // 组件的渲染模板
});
var myApp = new Vue({
el: '#myApp',
data: {
games:[
{title:"游戏名1", price:200},
{title:"游戏名2", price:300},
{title:"游戏名3", price:500},
]
}
});
</script>
</body>
</html>
过滤器属性
filter,做一些变量数据格式化的操作。例如数值的简单再计算,日期的格式调整,字母大小写之类
<!DOCTYPE html>
<html>
<head>
<title>freeknight Vue test 6</title>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
<div id='myApp'>
<p>{{ message }}</p>
<p>{{ message | toupper }}</p>
<p>{{ floatValue }}</p>
<p>{{ floatValue | topercent }}</p>
</div>
<script>
var myApp = new Vue({
el: '#myApp',
data: {
message: "Hello, world!",
floatValue: 0.3,
},
filters:{
toupper: function(value){
return value.toUpperCase(); // 最终显示 HELLO, WORLD!
},
topercent: function(value){
return value * 100 + "%"; // 最终显示 30%
}
}
});
</script>
</body>
</html>
计算属性
computed,对数据进行计算加工后得到新的数据在进行显示,和filter是类似的
<!DOCTYPE html>
<html>
<head>
<title>freeknight Vue test 7</title>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
<div id='myApp'>
<p>{{ floatValue }}元</p>
<p>{{ USDlloar }}美元</p>
</div>
<script>
var myApp = new Vue({
el: '#myApp',
data: {
floatValue: 513,
},
computed:{
USDlloar: function(){
return Math.round(this.floatValue / 7.1);
},
}
});
</script>
</body>
</html>
观察一个属性
watch 监视一个变量,一旦变化,则做出事件触发。
<!DOCTYPE html>
<html>
<head>
<title>freeknight Vue test 8</title>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
<div id='myApp'>
<p>{{ floatValue }}元</p>
<p>{{ USDlloar }}美元</p>
<button @click="btnClick(100)">加100块</button>
</div>
<script>
var myApp = new Vue({
el: '#myApp',
data: {
floatValue: 513,
USDlloar: 0,
},
watch: {
// 当 floatValue 的值发生变化,会执行该变量的watch函数
floatValue: function(newVal, oldVal){
console.log(oldVal, '->', newVal);
this.USDlloar = Math.round(this.floatValue / 7.1);
}
},
methods:{
btnClick: function(newVal){
this.floatValue += newVal;
}
}
});
// 进行初始化,这个赋值也会激活watch
myApp.floatValue = 515;
</script>
</body>
</html>
设置计算属性
setter 可以对计算属性进行修改时获得事件触发。
<!DOCTYPE html>
<html>
<head>
<title>freeknight Vue test 9</title>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
<div id='myApp'>
<p>{{ floatValue }}元</p>
<p>{{ USDlloar }}美元</p>
<p>{{ USDlloarSetter }}美元</p>
<button @click="btnClick(100)">加价100元</button>
<button @click="btnClickUSD(100)">加价100刀</button>
</div>
<script>
var myApp = new Vue({
el: '#myApp',
data: {
floatValue: 513,
},
computed:{
// 这种函数方式,只适合单项计算,不适合反向影响
USDlloar: function(){
return Math.round(this.floatValue / 7.1);
},
// 这个对象方式,可以双向影响。在 get, set 时均会触发事件
USDlloarSetter: {
get: function(){
return Math.round(this.floatValue / 7.1);
},
set: function(value){
this.floatValue = Math.round(value * 7.1);
}
},
},
methods:{
btnClick: function(newVal){
this.floatValue += newVal;
},
btnClickUSD: function(newVal){
this.USDlloarSetter += newVal;
},
}
});
</script>
</body>
</html>
属性绑定
v-bind为一个标签的一个属性绑定变量。
<!DOCTYPE html>
<html>
<head>
<title>freeknight Vue test 10</title>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<link href="myCss.css" rel="stylesheet">
</head>
<body>
<div id='myApp'>
<!-- 在 :冒号后面是要绑定的属性,这里我们绑定的是 class 类-->
<!-- 当isActive变量为true时,本 div 的class则为 active, 受到css影响,字体为红色 -->
<!-- 当isActive变量为flase时,本 div 的class则为 空, 不受到css影响,字体为默认黑色 -->
<div v-bind:class="{active:isActive}"> 红色文本1 </div>
<!-- 可以简写为 :attribute -->
<div :class="{active:isActive}"> 红色文本2 </div>
<div>
<!-- 这里我们绑定的是 提示信息 属性-->
<a href="#" :title="hintText">鼠标移过来看看</a>
</div>
<button @click="btnClick">更变状态</button>
</div>
<script>
var myApp = new Vue({
el: '#myApp',
data: {
isActive: true,
hintText: "这是提示信息",
},
methods:{
btnClick: function(){
this.isActive = (!this.isActive);
this.hintText += "@";
}
}
});
</script>
</body>
</html>
body{
font-size: 24px;
}
.active{
color: red;
}
类对象绑定
和上面差不多,只是不再绑定单一属性,而是绑定一系列属性的对象
<!DOCTYPE html>
<html>
<head>
<title>freeknight Vue test 11</title>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<link href="myCss.css" rel="stylesheet">
</head>
<body>
<div id='myApp'>
<!-- 在 :冒号后面是要绑定的属性,这里我们绑定的是 class 类-->
<!-- 当isActive变量为true时,本 div 的class则为 active, 受到css影响,字体为红色 -->
<!-- 当isActive变量为flase时,本 div 的class则为 空, 不受到css影响,字体为默认黑色 -->
<div v-bind:class="myclass"> 红色文本1 </div>
<!-- 可以简写为 :attribute -->
<div :class="myclass"> 红色文本2 </div>
<button @click="btnClick">更变状态</button>
</div>
<script>
var myApp = new Vue({
el: '#myApp',
data: {
myclass:{
active: true,
bigfont: true,
}
},
methods:{
btnClick: function(){
this.myclass.active = (!this.myclass.active);
this.myclass.bigfont = (!this.myclass.bigfont);
}
}
});
</script>
</body>
</html>
body{
font-size: 24px;
}
.active{
color: red;
}
.bigfont{
font-weight: bolder;
font-size: 32px;
}
条件渲染
v-if, v-else-if, v-else
<!DOCTYPE html>
<html>
<head>
<title>freeknight Vue test 12</title>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
<div id='myApp'>
<h1 v-if="result == 0">无成绩</h1>
<h1 v-else-if="result >= 80">考的很好</h1>
<h1 v-else-if="result < 60">没及格</h1>
<h1 v-else>考的一般</h1>
<button @click="btnClick">考试成绩</button>
</div>
<script>
var myApp = new Vue({
el: '#myApp',
data: {
result: 0,
},
methods:{
btnClick: function(){
this.result = Math.round(Math.random() * 100);
}
}
});
</script>
</body>
</html>
元素显示
v-show 标记是否显示,注意,不显示的依然会在dom树中。和上面的 v-if不一样
<!DOCTYPE html>
<html>
<head>
<title>freeknight Vue test 13</title>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
<div id='myApp'>
<h1 v-show="result">这个 h1 标签永久存在。</h1>
<button @click="btnClick">修改可见</button>
</div>
<script>
var myApp = new Vue({
el: '#myApp',
data: {
result: true,
},
methods:{
btnClick: function(){
this.result = !this.result;
}
}
});
</script>
</body>
</html>
对象迭代
v-for 和前面的一个v-for区别是,之前处理的是数组,这次处理的是一个对象。这个方法很适合进行debug。
<!DOCTYPE html>
<html>
<head>
<title>freeknight Vue test 14</title>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
<div id='myApp'>
<h3>js对象迭代</h3>
<div v-for="(value, key) in games">{{key}}:{{value}}</div>
</div>
<script>
var myApp = new Vue({
el: '#myApp',
data: {
isCanBeSeen: false,
games:{
title: "名字",
price: 200,
}
}
});
</script>
</body>
</html>
事件处理
v-on:{event} 简写 @{event} 当用户和页面交互时,获取用户事件信息。
<!DOCTYPE html>
<html>
<head>
<title>freeknight Vue test 15</title>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
<div id='myApp'>
<input id="myInput" v-on:keyup="myInputKeyup($event)">
<button id="myButton" @click="btnClick($event)">ok</button>
</div>
<script>
var myApp = new Vue({
el: '#myApp',
data: {
},
methods:{
myInputKeyup: function(event){
this.debugLog(event);
},
btnClick: function(event){
this.debugLog(event);
},
debugLog: function(event){
console.log("组件类型:", event.srcElement.tagName,
"组件ID:", event.srcElement.id,
"组件数据:", event.srcElement.innerHTML,
"用户按键:", event.key? event.key: "无")
}
}
});
</script>
</body>
</html>
表单控件绑定
这个非常重要。
v-model 给组件绑定一个数据,注意,这里是双向绑定。
<!DOCTYPE html>
<html>
<head>
<title>freeknight Vue test 16</title>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
<div id='myApp'>
<!-- 注意:这里都是双向绑定。即,若修改了input中的数据,message变量值也发生了改变 -->
<!-- 输入框 -->
<input type="text" v-model="message" placeholder="没东西">
<p>Message 变量值是 {{message}}</p>
<div></div>
<!-- 多选框 -->
<input type="checkbox" id="cb1" value="第一项的值" v-model="checkboxValue">
<label for="cb1">第一项</label>
<input type="checkbox" id="cb2" value="第二项的值" v-model="checkboxValue">
<label for="cb2">第二项</label>
<input type="checkbox" id="cb3" value="第三项的值" v-model="checkboxValue">
<label for="cb3">第三项</label>
<p>checkboxValue 变量值是 {{checkboxValue}}</p>
<div></div>
<!-- 单选框-->
<input type="radio" id="r1" value="第1项的值" v-model="singleCheckboxValue">
<label for="r1">第1项</label>
<input type="radio" id="r2" value="第2项的值" v-model="singleCheckboxValue">
<label for="r2">第2项</label>
<input type="radio" id="r3" value="第3项的值" v-model="singleCheckboxValue">
<label for="r3">第3项</label>
<p>singleCheckboxValue 变量值是 {{singleCheckboxValue}}</p>
<div></div>
<!-- 单选下拉框 -->
<select v-model="selectValue">
<option v-for="question in questionsValues">{{question}}</option>
</select>
<p>selectValue 变量值是 {{selectValue}}</p>
<div></div>
<!-- 复选下拉框 -->
<select v-model="selectValues" multiple style="height: 70px;">
<option v-for="question in questionsValues">{{question}}</option>
</select>
<p>selectValues 变量值是 {{selectValues}}</p>
</div>
<script>
var myApp = new Vue({
el: '#myApp',
data: {
message: "Hello, world!",
checkboxValue: [],
singleCheckboxValue: "",
selectValue: "",
selectValues: [],
questionsValues :[
"问题1",
"问题2",
"问题3",
"问题4",
"问题5",
]
}
});
</script>
</body>
</html>
表单控件修饰符
.lazy
此时用户输入不再做绑定数据params的实时更新处理,仅在控件的onChange事件中更新该变量。目的是提高页面性能。
.number
将用户输入内容转换为数值类型,若输入为非数值,则返回NaN
.trim
自动去除用户输入内容两端的空格
<!DOCTYPE html>
<html>
<head>
<title>freeknight Vue test 17</title>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
<div id='myApp'>
<h1>用户注册</h1>
<div>
<label for="username">用户:</label>
<input type="text" id="username" v-model.lazy="username" @change="checkUsername">
<span v-if="isUsernameOK">可以注册</span>
</div>
<div>
<label for="age">年龄:</label>
<input type="number" id="age" v-model.number="age">
</div>
<div>
<label for="content">个人看法: </label>
<textarea id="content" v-model.trim="content" cols="55" rows="8"></textarea>
</div>
<h4>信息提示</h4>
<p>{{username}}</p>
<p>{{age}}</p>
<p>{{content}}</p>
</div>
<script>
var myApp = new Vue({
el: '#myApp',
data: {
username: "",
isUsernameOK: false,
age: "",
content: ""
},
methods:{
checkUsername: function(){
if(this.username.length <= 0){
this.isUsernameOK = false;
}else{
this.isUsernameOK = true;
}
}
}
});
</script>
</body>
</html>
组件作用域
Component 一个区域内容,类似自定义一个标签。
Portlet 某一个页面功能
<!DOCTYPE html>
<html>
<head>
<title>freeknight Vue test 18</title>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
<div id='myApp'>
<today-weather></today-weather>
<another-weather></another-weather>
</div>
<script>
// 注册一个全局的组件,不属于 myApp,而是属于VUE全局,注意:全局组件必须声明在 new Vue() 之前。
Vue.component('today-weather',{
template: '<div>天气预报模板</div>'
});
// 可复用的局部组件,声明为一个普通的js对象
var anotherWeatherComponent = {
template: '<div>另外一个可复用的天气预报模板</div>'
};
var myApp = new Vue({
el: '#myApp',
components:{
// 绑定HTML标签和组件模板对象,这个component仅仅可被本 myApp 内使用。
'another-weather': anotherWeatherComponent
},
});
</script>
</body>
</html>v
特殊的组件
因为HTML的限制,部分元素只能在浏览器解析HTML之后才会去获取组件模板内容,典型的例如表格,排序组件 ol ul table select option 等元素。
如果写成
<table>
<my-compenent></my-compenent>
</table>
是不能显示正确位置的。需要使用特殊的 is 属性如下:
<table>
<tr is='my-compenent'></tr>
</table>
组件中的数据
组件中的变量数据,不能使用data属性,只能使用data函数。
<!DOCTYPE html>
<html>
<head>
<title>freeknight Vue test 19</title>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
<div id='myApp'>
<div>
今天的天气是:<today-weather></today-weather>
明天的天气是:<tomorrow-weather></tomorrow-weather>
</div>
</div>
<script>
Vue.component("today-weather",{
template: "<strong>{{todayWeatherValue}}</strong>",
data:{
todayWeatherValue: '大雪', // 这是一个数据data属性,将会js报错。
}
});
Vue.component("tomorrow-weather",{
template: "<strong>{{tomorrowWeatherValue}}</strong>",
data: function(){
return{
tomorrowWeatherValue: '晴天', // 这是一个data函数,这样则是正确的
};
}
});
var myApp = new Vue({
el: '#myApp',
});
</script>
</body>
</html>
组件间的数据传递
props 接收外界数据或变量
<!DOCTYPE html>
<html>
<head>
<title>freeknight Vue test 20</title>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
<div id='myApp'>
<text-result :score="50"></text-result>
<text-result :score="70"></text-result>
<text-result :score="100"></text-result>
<hr/>
<div>请输入名字: <input v-model="yourname"></div>
<say-hello :pname="yourname">
</div>
<script>
Vue.component('text-result', {
props: ['score'], // 这里是接数据
template: '<div><strong>{{score}}分 {{result}}</strong></div>',
computed:{ // 顺道复习conputed
result: function(){
var strResult = "未知";
if(this.score < 60){
strResult = "不及格";
}else if(this.score >= 80){
strResult = "成绩很好";
}else{
strResult = "马马虎虎";
}
return strResult;
}
}
});
Vue.component('say-hello',{
props: ['pname'], // 这里接的是变量
template: '<div><strong> hello, {{pname}} </strong></div>'
});
var myApp = new Vue({
el: '#myApp',
data:{
yourname: "freeknight"
}
});
</script>
</body>
</html>
参数检查验证
type, validator, default
<!DOCTYPE html>
<html>
<head>
<title>freeknight Vue test 1</title>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
<div id='myApp'>
<show-member-info name="freeknight" :age="18" :detail="{address:'singaporo', language:'日语'}">
</show-member-info>
</div>
<script>
Vue.component('show-member-info', {
props:{
name: {
type: String,
required: true, // 该组件的该参数项为强制存在
},
age:{
type: Number,
validator: function(value){ // 有效性校验
return value >= 0 && value <= 100;
}
},
detail:{
type: Object,
default: function(){ // 默认值
return {
address: 'NoPlace',
language: '英语'
}
}
}
},
template: '<div>名字: {{this.name}} <br/>'
+ '年龄: {{this.age}} <br/>'
+ '地址: {{this.detail.address}} <br/>'
+ '语言: {{this.detail.language}} </div>'
});
var myApp = new Vue({
el: '#myApp',
});
</script>
</body>
</html>
事件的传递
v-on 监听消息,$emit 提交事件
<!DOCTYPE html>
<html>
<head>
<title>freeknight Vue test 22</title>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
<div id='myApp'>
<add-method :a="6" :b="12" v-on:add_event="getAddResult"></add-method>
<hr/>
<h3>{{result}}</h3>
</div>
<script>
Vue.component('add-method',{
props: ['a', 'b'],
// 按钮,点击事件为add
template: '<div><button v-on:click="add">加</button></div>',
methods:{
add: function(){
var value = 0;
value = this.a + this.b;
console.log(value);
// 提交一个add_event事件
this.$emit( 'add_event', {
result: value
});
}
}
});
var myApp = new Vue({
el: '#myApp',
data: {
result: 0
},
methods:{
getAddResult: function(val){
this.result = val.result;
}
}
});
</script>
</body>
</html>
slot插槽
slot 是父附件和子附件性通讯的方式。
<!DOCTYPE html>
<html>
<head>
<title>freeknight Vue test 23</title>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
<div id='myApp'>
<say-to pname="小A">你吃了没</say-to>
<say-to pname="小B">你饿了没</say-to>
<say-to pname="小C">你给他们做饭吧</say-to>
<nba-all-stars c="奥尼尔" pf="加内特">
<span slot="sf">皮克</span>
<span slot="sg">丘吉尔</span>
<span slot="pg">特朗普</span>
</nba-all-stars>
</div>
<script>
Vue.component('say-to',{
props: ['pname'],
template: '<div> 你好, {{pname}}! '
+ '<slot></slot>' // 这里,将上面标签中的内容"你吃了没""你饿了没"等,嵌入到组件内容中了
+ '</div>',
});
Vue.component('nba-all-stars',{
props: ['c', 'pf'], // 这是普通组件属性
template: '<div>'
+'<p>中锋: {{c}}</p>'
+'<p>大前: {{pf}}</p>'
+'<p>小前: <slot name="sf"></slot></p>' // 带标签名的slot
+'<p>分卫: <slot name="sg"></slot></p>'
+'<p>控卫: <slot name="pg"></slot></p>'
+'</div>',
});
var myApp = new Vue({
el: '#myApp',
});
</script>
</body>
</html>
Vue cli安装
c:\Users\freeknight>npm -v // 查看npm版本
6.14.4
c:\Users\freeknight>npm show vue-cli // 查看npm远程仓库中的vue-cli版本
报错…… npm ERR! code ECONNRESET request to https://registry.npmjs.org/vue-cli failed..
c:\Users\freeknight>npm config set registry http://registry.npmjs.org/ // 将npm请求从https改为http
c:\Users\freeknight>npm show vue-cli // 这次OK了
vue-cli@2.9.6 | MIT | deps: 20 | versions: 35
dist-tags:
latest: 2.9.6
published a year ago by yyx990803 <yyx990803@gmail.com>
c:\Users\freeknight>npm install -g vue-cli@2.9.6 // 下载vue
+ vue-cli@2.9.6
added 238 packages from 205 contributors in 62.513s
c:\Users\freeknight>vue -V // 查看本地vue版本
2.9.6
c:\Users\freeknight>vue -h // 查看帮助
Usage: vue <command> [options]
Options:
-V, --version output the version number
-h, --help output usage information
Commands:
init generate a new project from a template // 通过模板去初始化一个工程
list list available official templates // 查看可用的官方模板列表,官方模板到 https://github.com/vuejs-templates 这里看就好
build prototype a new project // 发包发布自己的项目
create (for v3 warning only)
help [cmd] display help for [cmd]
c:\Users\freeknight>vue webpack myProjectName // 使用webpack默认模板去创建一个叫myProject的项目
c:\Users\freeknight>vue username/repo myMyProjectName // 使用 GitHub 仓库做模板创建
webpack模板工程
其详细文档可参见: http://vuejs-templates.github.io/webpack/prerender.html
c:\Users\freeknight>vue init webpack myweb // 创建webpack的模板工程
? Project name myweb // 工程名确认
? Project description A Vue.js project // 描述文字确认
? Author d******8@gmail.com // 开发者邮箱确认
? Vue build
> Runtime + Compiler: recommended for most users // 注意:建议选这个带compiler编译器的。
? Vue build standalone
? Install vue-router? Yes
? Use ESLint to lint your code? Yes
? Pick an ESLint preset Standard
? Set up unit tests No
? Setup e2e tests with Nightwatch? No
? Should we run `npm install` for you after the project has been created? (recommended) npm
vue-cli · Generated "myweb".
// One thousand years later......
// One thousand years later......
// One thousand years later......
# Project initialization finished!
# ========================
To get started:
cd myweb
npm run dev
c:\Users\freeknight>cd myweb // 进入项目文件夹
c:\Users\freeknight>npm run dev // 其实是在执行 package.json 中的scripts对象中的 dev 脚本。
DONE Compiled successfully in 21276ms 22:04:36
I Your application is running here: http://localhost:8080
可以本地浏览器打开 http://localhost:8080 查看
c:\Users\freeknight>npm run build // 打包发布输出,其实是在执行 package.json 中的scripts对象中的 build 脚本。
会将发布版本生成到dist目录下。使用该目录进行发布即可
该项目工程中最重要的结构依然是
src 源文件文件夹
dist 打包发布文件夹
package.json 项目工程设置文件
VUE项目中导入bootstrap4
引入bootstrap目的是让网页更加漂亮。
$cd myweb
$npm install bootstrap --save --save-exact // 安装bootstrap,--save会将其直接加入到package.json项目文件中 --save-exact表示其版本号前不再有 "dependencies": {
"bootstrap": “^4.4.1”} 的这个 ^ 符号,表示版本指定,不允许智能调整。
+ bootstrap@4.4.1
added 1 package from 2 contributors and audited 12619 packages in 66.6s
安装完毕bootstrap后,找到myweb目录下的src目录中的main.js,这个是文件入口。 在这里导入bootstrap,添加以下一行
import 'bootstrap/dist/css/bootstrap.min.css'
然后就可以使用bootstrap了。 如果我们想测试是否导入bootstrap成功,可以修改 src 目录中的 app.vue 入口模板文件做个测试
<template>
<div id="app">
<img src="./assets/logo.png">
<hr/>
<button class="btn btn-success">测试bootstrap</button> // 这里加入bootstrap的CSS样式
<button class="btn btn-danger">测试bootstrap</button> // 这里加入bootstrap的CSS样式
<hr/>
<router-view/>
</div>
</template>
执行npm start,你本地浏览器看看按钮的效果就知道了。(或者移除上面的import,可以进行对比)
当然,这是传统的方式使用bootstrap+VUE,更好的方式是使用 https://bootstrap-vue.org/ ,它提供了一系列的bootstrap的组件tag,例如更美观的按钮
<b-button variant="success">新样式button</b-button>
VUE项目中引入Ajax库 Axios
引入ajax的目的是,从服务器取得数据,使网页有远程存取能力。
axios不仅支持浏览器,也可用于node.js开发。
$cd myweb
$npm install --save --save-exact axios vue-axios // 这里安装了两个库,一个axios基本库,一个vue-axios封装的vue组件,可便于我们在vue中使用axios
+ axios@0.19.2
+ vue-axios@2.1.5
added 4 packages from 8 contributors, updated 1 package and audited 12624 packages in 47.866s
安装完毕后,一样找到src目录下的main.js,进行注册。
import axios from 'axios'
import VueAxios from 'vue-axios'
// 注意,它还需要指定vue使用
Vue.use(VueAxios, axios)
然后我们测试是否成功,可以找到src目录下的components中的 helloworld.vue,直接复制下面内容覆盖helloworld.vue即可。
<!-- HTML部分,我给基本清理了 -->
<template>
<div class="hello">
<pre>{{content}}</pre> <!-- 显示的组件 -->
</div>
</template>
<!-- JS部分 -->
<script>
export default { /* 这里表示导出helloworld这个组件 */
name: 'HelloWorld',
data () {
return {
content: '没有内容'
}
},
// 该页面加载完毕后,自动执行的函数 mounted
mounted () {
var myApiAddress = 'https://newsapi.org/v2/top-headlines'
this.axios.post(myApiAddress).then(
body => {
this.content = body.data /* 将从API请求的数据返回,设置到content中并显示出来 */
}
)
}
}
</script>
<!-- CSS样式部分,内容我给删除了 -->
<!-- 添加 "scoped" 属性表示这个css样式仅仅作用于当前组件,不会影响其他组件 -->
<style scoped>
</style>
VUE自定义样式单
- 进入assets目录,创建mycss.css文件
.myClass1 {
color: red;
border: 1px solid blue;
}
- 打开app.Vue
<script>
import './assets/mycss.css' // 添加自定义css文件
export default {
name: 'App',
data(){
return "";
}
}
</script>
- 添加测试
<template>
<div id="app">
<img src="./assets/logo.png">
<div class='myClass1'>你好</div>
<router-view/>
</div>
</template>
webpack项目工程结构
.
├── build/ # webpack配置文件,通常不应该修改。如果你想自定义webpack loader,可以看webpack.base.conf.js
│ └── ...
├── config/ # 工程设置文件 【发布测试运维时需要处理这里】
│ ├── index.js # 核心项目设置文件
│ └── ...
├── dist/ # 最终生成发布目录,执行 npm run build得到
├── src/ # 最主要代码工作文件夹
│ ├── main.js # app入口函数
│ ├── App.vue # app主组件
│ ├── components/ # 组件文件夹 【95%时间在这里】
│ │ └── ... # 最核心开发文件夹。一个.vue文件对应一个组件,一个组件包括 template(HTML内容),script(组件js脚本),style(组件css样式)
│ └── assets/ # 组件资源目录,例如图片,全局css等
│ └── ...
├── static/ # 静态资源,这里面的资源在最后发布时会直接被拷贝到发布文件夹。一般是视频啦,提供用户下载的可执行文件之类
├── test/ # 测试相关目录文件夹
│ └── unit/ # unit 单元测试文件夹
│ │ ├── ...
│ └── e2e/ # e2e 测试文件夹
│ │ ├── ...
├── .babelrc # babel 配置文件
├── .editorconfig # 开发ide的配置文件
├── .eslintrc.js # eslint 配置
├── .eslintignore # eslint 规则例外文件
├── .gitignore # git 管理例外文件
├── .postcssrc.js # postcss 配置文件
├── index.html # index.html的一个单页面模板,整个程序的真正入口。它调src/main.js,然后main.js加载了最基本的一个组件叫App.vue,并且使用src/router加载大量子页面作为二级菜单三级菜单,每个子菜单页面对应各个不同的component以及子component。
├── package.json # npm包依赖管理和构建密码命令 【前期添加依赖时要关注这里】
└── README.md
VUE路由router
https://router.vuejs.org/ 可以看中文文档 https://router.vuejs.org/zh/
它是用来做网页页面路由的。
安装 很简单,但一般情况下都是安装好的,并不需要手动处理。
$npm install vue-router --save --save-exact
使用 的时候需要查看 src/main.js
import router from './router'
// 这里是入口组件,在这里使用router对象,则意味着所有子组件都可以使用到router对象
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
以及 src/router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld' // 添加我们的组件
import HelloWorld2 from '@/components/HelloWorld2'
Vue.use(Router)
// 输出url映射类 Router
export default new Router({
routes: [
{
path: '/', // 完成一个url和一个组件之间的映射
name: 'HelloWorld',
component: HelloWorld
},
{
path: '/Helloworld2',
name: 'HelloWorld2',
component: HelloWorld2
}
]
})
如果说 主页面组件中 需要有HelloWorld2这个子页面组件的跳转链接,那么可以修改 主页面.Vue 如下。
<template>
<div id=app>
<p>
<!-- 这就类似于 a 标签和 ref 属性 -->
<router-link to='/'>HOME</router-link>
<router-link to='/Helloworld2'>访问helloworld</router-link>
</p>
<router-view/>
</div>
</template>
动态路由
就是路由URL会发生一些变化,例如,http://xxoo.com/player/1 http://xxoo.com/player/2 打开两个不同的页面,但 1,2 这些动态URL内路径不能枚举写死。
创建components/Player.vue 组件
<template>
<div>
<h1>球员页面</h1>
<p>{{detail}}</p>
</div>
</template>
<script>
export default {
name: 'Player',
data() {
return {
detail: {}
}
},
mounted() {
// formLoad初始化时,通过player uid获取球员信息
this.detail = this.getPlayerDetail(this.$route.params.uid);
},
beforeRouteUpdate(to, from, next){
// route发生更变之前的回调消息,如果没有该函数,则会在切换网页后,detail不变的情况。
// 因为mounted是init行为,当页面渲染完毕后,不会再次重复被调用
this.detail = this.getPlayerDetail(to.params.uid);
next(); // 事件提交给系统处理
},
methods:{
getPlayerDetail(uid){
// 这里也可以使用 axios 从服务器获取
switch(uid){
case '1':
return {uid: 1, name: "德玛西亚", point:33 }
case '2':
return {uid: 2, name: "诺克萨斯", point:12 }
default:
return {uid: -1}
}
}
}
}
</script>
设置路由,修改router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import Player from '@/components/Player'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/player/:uid',
name: 'Player',
component: Player
}
]
})
修改App.vue进行测试,传入带不同路径的URL
<template>
<div id="app">
<p>
<router-link to="/">主页</router-link>
<router-link to="/player/1">德玛西亚</router-link>
<router-link to="/player/2">诺克萨斯</router-link>
</p>
<router-view/>
</div>
</template>
<script>
import './assets/mycss.css' // 添加自定义css文件
export default {
name: 'App'
}
</script>
嵌套路由
在动态路由后面添加新的URL,例如:http://xxoo.com/player/1/profile http://xxoo.com/player/2/stats 这样的URL。
创建components/Player/Profile.vue 组件
<template>
<div>
<h2>球员介绍: {{$route.params.uid}}</h2>
</div>
</template>
创建components/Player/Stats.vue 组件
<template>
<div>
<h2>球员数据: {{$route.params.uid}}</h2>
</div>
</template>
修改router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import Player from '@/components/Player'
import PlayerProfile from '@/components/Player/Profile'
import PlayerStats from '@/components/Player/Stats'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/player/:uid',
name: 'Player',
component: Player,
children:[
{
path: 'profile',
component: PlayerProfile,
},
{
path: 'stats',
component: PlayerStats,
},
]
}
]
})
修改component/player.vue,我们这里还实现了 组件内嵌套子组件 的功能。
<template>
<div>
<h1>球员页面</h1>
<p>{{detail.uid}}</p>
<p>{{detail.name}}</p>
<p>{{detail.point}}</p>
<hr/>
<!-- 这是在player.vue中嵌入子vue组件 -->
<!-- 这里绑定的是data中的两个变量profile, stats -->
<router-link :to="profile">简介</router-link>
<router-link :to="stats">数据</router-link>
<hr/>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'Player',
data() {
return {
detail: {},
profile: '',
stats: '',
}
},
mounted() {
// formLoad初始化时,通过player uid获取球员信息
this.detail = this.getPlayerDetail(this.$route.params.uid);
this.profile = '/player/' + this.$route.params.uid + '/profile';
this.stats = '/player/' + this.$route.params.uid + '/stats';
},
beforeRouteUpdate(to, from, next){
// route发生更变之前的回调消息,如果没有该函数,则会在切换网页后,detail不变的情况。
// 因为mounted是init行为,当页面渲染完毕后,不会再次重复被调用
this.detail = this.getPlayerDetail(to.params.uid);
this.profile = '/player/' + to.params.uid + '/profile';
this.stats = '/player/' + to.params.uid + '/stats';
next(); // 事件提交给系统处理
},
methods:{
getPlayerDetail(uid){
// 这里也可以使用 axios 从服务器获取
switch(uid){
case '1':
return {uid: 1, name: "德玛西亚", point:33 }
case '2':
return {uid: 2, name: "诺克萨斯", point:12 }
default:
return {uid: -1}
}
}
}
}
</script>
路由编程
上面我使用 route-link 标记来生成页面中的标记,然后进行的url转向到其他的组件页面。我们也可以使用新的方式。router.push(location, onComplete?, onAbort?)
下面对App.vue做修改
<template>
<div id="app">
<p>
<router-link to="/">主页</router-link>
<router-link to="/player/1">德玛西亚1</router-link>
<router-link to="/player/2">诺克萨斯1</router-link>
</p>
<!-- to前面加冒号,后面则添加json对象,也就等同 router.push(...) -->
<router-link :to="{name: 'Player', params: {uid: 1}}">德玛西亚2</router-link>
<router-link :to="{path: '/player/2/stats'}">诺克萨斯2</router-link>
<hr/>
<button @click="btnClick(1)">德玛西亚3</button>
<button @click="btnClick(2)">诺克萨斯3</button>
<router-view/>
</div>
</template>
<script>
import './assets/mycss.css' // 添加自定义css文件
export default {
name: 'App',
methods:{
btnClick(uid){
console.log(uid);
// 按键之后,进行path的重定向
this.$router.push({path: '/player/${uid}'})
// 重定向方式2
// this.$router.push({name: 'Player', params: {uid: uid}})
// 重定向方式3 ,这样访问的则是 http://xxoo.net/player?uid=1
// this.$router.push({name: 'Player', query: {uid: uid}})
}
}
}
</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>
单路由多view
一个页面 (也就是一个路由) 中组合多个组件。
router-view[name] 绑定
components : 注意有s
// index.js
import SettingDetail from '@/components/Setting/Detail'
import SettingHeader from '@/components/Setting/Header'
import SettingSideBar from '@/components/Setting/SideBar'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Home',
components: {
myHeader: SettingHeader,
mySideBar: SettingSideBar,
myDetail: SettingDetail,
}
}
]
})
App.vue
<template>
<div id="app">
<table>
<tr>
<td colspan="2" style="background-color:darkgoldenrod">
<router-view name="myHeader"></router-view>
</td>
</tr>
<tr>
<td width="20%" style="background-color:thistle">
<router-view name="mySideBar"></router-view>
</td>
<td width="20%" style="background-color:aquamarine">
<router-view name="myDetail"></router-view>
</td>
</tr>
</table>
</div>
</template>
URL重定向
alias: URL别名
redirect: 重定向
//index.js
export default new Router({
routes: [
{
path: '/player/:uid',
name: 'Player',
// 此时输入 htttp://xxoo.com/about/1 等同于 http://xxoo.com/player/1
alias: 'about',
component: Player,
children:[
{
path: 'profile',
component: PlayerProfile,
},
{
path: 'stats',
component: PlayerStats,
},
]
},
{
path: '/',
name: 'Home',
components: {
myHeader: SettingHeader,
mySideBar: SettingSideBar,
myDetail: SettingDetail,
}
},
{
path: '/hi', // 这里可以使用 * 通配
name: 'hi',
// 输入 http://xxxoo.com/hi 等同于输入 http://xxxoo.com/player/2 ,会自动重定向
redirect: '/player/2',
}
]
})
多参数路由
路由传递多个属性props
user.vue
<template>
<div>
<h1>
测试url多参
</h1>
<p>uid={{ uid }}, nationality={{ nationality }}</p>
<!-- 这里可以有上下两种方式去获取参数 -->
<p>$route.params.uid={{ $route.params.uid }}</p>
<p>$route.params.nationality={{ $route.params.nationality }}</p>
</div>
</template>
<script>
export default {
name: "User",
// 这里表示从 params 里接受两个参数
props: ['uid', 'nationality']
}
</script>
index.js 添加route
{
path: '/user/:uid/:nationality',
name: 'User',
component: User,
props: true,
}
App.vue
<p>
<router-link to="/user/1/USA">User1</router-link>
<router-link to="/user/2/china">User2</router-link>
</p>