断断续续学了好长好长时间了,这次又开始了,坚持学完?
Vue2.5-2.6-3.0开发去哪儿网App 零基础入门到实战项: https://coding.imooc.com/class/203.html
1.1 课程介绍
中文文档完善/代码轻量/上手简单/容易使用
NUXT 服务器渲染
weex vue语法编写原生app
-
基础内容
- 基础语法
- MVVM模式
- 前端组件化
- 声明周期函数
- 动画特效
-
实战项目
- 环境搭建
- 使用git
- 数据模拟
- 本地开发
- 前后端代码联挑
- 真机测试
- 上线
实战项目介绍
多区域轮博/多区域展示/城市展示/城市搜索/右侧字母与左侧联动…..
公用的画廊组件/递归展示的列表组件…
使用的内容
- Axios 数据获取
- Vuex
- Vue Router
- 异步组件
- Stylus
- 递归组件
- 插件 swiper…
- 公用组件
课程安排
- 第1章 课程介绍
- 第2章 Vue初探
- 第3-5章 基础知识精讲
- 第6-9章 Vue项目实战
- 第10章 项目测试上线流程几后续学习指南
学习前提
- js
- es6
- webpack
- npm
收获
- 入门Vue的使用
- 理解vue开发流程
- 移动端布局技巧
- 上手公司中型/大型项目
- 规范代码编写
本章将快速讲解部分 Vue 基础语法,通过 TodoList 功能的编写,在熟悉基础语法的基础上,扩展解析 MVVM 模式及前端组件化的概念及优势。
2-1 课程学习方法
最好的学习资料就是官方文档
视频+官方文档 进行学习
2-2 hello world
- 开发版 – 代码提示 代码报错
- 生产版
Vue 不支持 IE8 及以下版本,因为 Vue 使用了 IE8 无法模拟的 ECMAScript 5 特性。但它支持所有兼容 ECMAScript 5 的浏览器。
2-3 开发TodoList(v-model、v-for、v-on)
标签指令
- v-for
- v-on
- v-model
MVVM 不关心DOM,只关注数据,数据发生改变,DOM结构就发生改变
2-4 MVVM模式
MVP设计模式
面向DOM开发
大多时间在操作DOM结构
<div>
<input type="text" id="input">
<button id="btn">提交</button>
<ul id="list">
</ul>
</div>
<script>
// 构造函数
function Page() {}
$.extend(Page.prototype, {
init: function () {
this.bindEvents()
},
bindEvents: function () {
var btn = $("#btn");
btn.on("click", $.proxy(this.handleBtnClick, this))
},
handleBtnClick: function () {
var inputElem = $("#input");
var inputVal = inputElem.val();
var eulElem = $("#list");
if (inputVal == '') {
alert("不能为空")
} else {
eulElem.append('<li>' + inputVal + '</li>')
inputElem.val('')
}
}
})
var page = new Page();
page.init()
</script>
MVVM设计模式
vue 就是 vm 层
面向数据开发
只需要修改M层数据,无需操作DOM
2-5 【讨论题】你所知道的常用设计模式有哪些?
2-6 前端组件化
组件:页面的一个部分,由原来的一个整体,切成一个一个的部分,每个部分可以称为一个组件
- 顶部轮播
- 搜索框
- 等等
都可以称之为一个组件
合理的拆分组件,可以把一个大型的项目像拼积木一样拼接起来
一个项目业务逻辑可能会很复杂,拆分成组件以后,每一个组件就会变得很轻巧,后期维护起来也会更加容易
每一个组件就是页面上的一个区域
2-7 使用组件改造TodoList
- 创建全局组件
Vue.component()
TodoItem == todo-item
<TodoItem></TodoItem>
等于
<todo-item></todo-item>
全局组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>全局组件</title>
<script src="./static/vue.js"></script>
</head>
<body>
<div id="root">
<div>
<input type="text" v-model="todoVal">
<button @click="handleBtnClick">提交</button>
</div>
<ul>
<!-- <li v-for="item in list">
{{item}}
</li> -->
<todo-item v-bind:content="item"
v-for="item in list">
</todo-item>
</ul>
</div>
<script>
Vue.component("TodoItem",{
props: ['content'],
template: "<li>{{content}}</li>"
})
new Vue({
el:"#root",
data: {
todoVal:"",
list: []
},
methods: {
handleBtnClick: function(){
this.list.push(this.todoVal)
this.todoVal = ''
}
},
})
</script>
</body>
</html>
局部组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>局部组件</title>
<script src="./static/vue.js"></script>
</head>
<body>
<div id="root">
<div>
<input type="text" v-model="todoVal">
<button @click="handleBtnClick">提交</button>
</div>
<ul>
<todo-item v-bind:content="item"
v-for="item in list">
</todo-item>
</ul>
</div>
<script>
var TodoItem = {
props: ['content'],
template: "<li>{{content}}</li>"
}
new Vue({
el:"#root",
data: {
todoVal:"",
list: []
},
methods: {
handleBtnClick: function(){
this.list.push(this.todoVal)
this.todoVal = ''
}
},
components: {
TodoItem:TodoItem
}
})
</script>
</body>
</html>
关于keys的问题暂时记录下,后期补充
2-8 简单的组件间传值
父向子传值
通过v-bind
传值,通过props
接收该值
子向父传值
添加todolist删除的功能
通过
$emit()
向外触发事件,父组件监听事件,监听的时候获取子组件带回的内容,实现子组件向父组件传值
v-bind简写 :
<todo-item v-bind:content="item"
v-bind:index="index"
v-for="(item,index) in list" // item 数组的内容;index数组的下标
@delete="handelItemDelete"
>
</todo-item>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>组件传值</title>
<script src="./static/vue.js"></script>
</head>
<body>
<div id="root">
<div>
<input type="text" v-model="todoVal">
<button @click="handleBtnClick">提交</button>
</div>
<ul>
<todo-item v-bind:content="item"
v-bind:index="index"
v-for="(item,index) in list"
@delete="handelItemDelete"
>
</todo-item>
</ul>
</div>
<script>
var TodoItem = {
props: ['content','index'], // 父传子接收
template: '<li @click="handleItemClick">{{content}}</li>',
methods: {
handleItemClick: function(){
// 向外触发一个事件,通过v-on监听事件
this.$emit("delete",this.index)
}
}
}
new Vue({
el:"#root",
data: {
todoVal:"",
list: []
},
methods: {
handleBtnClick: function(){
this.list.push(this.todoVal)
this.todoVal = ''
},
handelItemDelete: function(index){
console.log(index)
this.list.splice(index,1)
}
},
components: {
TodoItem:TodoItem
}
})
</script>
</body>
</html>
2-9 章节小结
- 通过
script
标签引入vuejs进行基础代码编写 - 数据的双向绑定
- 父子组件传值
阅读官网介绍部分内容:https://v2.cn.vuejs.org/v2/guide/index.html
个人总结
-
不支持ie8及以下版本,因为使用了 ECMAScript 5 特性,ie8及以下版本无法模拟这些特性
-
vuejs介绍
-
声明式渲染
- v-bind
-
条件与循环
- v-if ,dom节点不存在
- v-show ,dom节点存在,display:none / block
- v-for ,数据循环
-
处理用户输入
- v-on ,事件监听器
- v-model ,双向数据绑定
-
组件化应用构建
- 全局组件
- 局部组件
- 组件数据传递:父组件-> 子组件 (props)
- 组件数据传递:子组件-> 父组件 (v-bind,v-on)
本章通过精挑细选的案例,精讲 Vue 中的基础知识,包括实例、生命周期、指令、计算属性、方法、侦听器,表单等部分内容。
3-1 Vue实例
创建组件的时候,vue底层也是把他编译成vue的一个实例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue实例</title>
<script src="./static/vue.js"></script>
</head>
<body>
<div id="root">
<div v-on:click="handleBtnClick">
{{mess}}
</div>
<item></item>
</div>
<script>
Vue.component("item",{
template: "<div>hello item</div>"
})
var vm = new Vue({
el: "#root",
data: {
mess:"hello world"
},
methods: {
handleBtnClick: function(){
alert(this.mess)
}
},
})
</script>
</body>
</html>
vm
一般指一个vue
的实例el
接管的标签data
存放的数据{{mess}}
插值表达式- 事件必须定义在
methods
里面 v-on
简写@
- 除了
el
,data
,methods
方法, 还有其他的,比如props
,watch
… - 一个
vue
项目是有很多个小的组件(实例)组成的 vm.$
凡是$
开头的一般指vue
的实例属性(方法),例如vm.$data
,vm.$el
…vm.$destroy
销毁实例,销毁后数据发生变化的时候,页面不会跟着变,但是 之前的事件还是存在的
3-2 Vue实例生命周期
生命周期函数,就是vue实例在 某一个时间 点自动执行的函数
不行药手动的去调用,vue的内部会在某个时刻自动的去执行这些函数
beforeCreate
: 无法通过实例访问data数据和methods方法created
: 可以通过实例访问data数据和methods方法beforeMount
: hello world 并未渲染到页面上mounted
: hellow world 已经渲染到页面上beforeDestroy
: 实例销毁之前执行destroyed
: 实例销毁之后执行beforeUpdate
: 数据发生改变,还未渲染之前updated
: 数据发生改变,重新渲染之后
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>生命周期</title>
<!-- <script src="./static/vue.js"></script> -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h1>通过控制台查看生命周期函数的执行过程</h1>
<div id="mydomid">
{{number}}
</div>
<input type="text" v-model="number"><hr>
<button @click="upDataClick">更新数据</button>
<button @click="delVueClick">销毁实例</button>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
mess:"hello world",
number: 0
},
methods: {
upDataClick: function(){
this.number ++
},
delVueClick: function(){
this.$destroy()
}
},
beforeCreate: function(){ //无法通过实例访问data数据和methods方法
console.table([{
生命周期: "beforeCreate",
解释说明: "无法通过实例访问data数据和methods方法",
时间戳: new Date().getTime(),
data数据: this.number,
渲染数据: document.getElementById("mydomid").innerText,
挂载节点:this.$el
}])
},
created: function(){ //可以通过实例访问data数据和methods方法
console.table([{
生命周期: "created",
解释说明: "可以通过实例访问data数据和methods方法",
时间戳: new Date().getTime(),
data数据: this.number,
渲染数据: document.getElementById("mydomid").innerText,
挂载节点:this.$el
}])
},
beforeMount: function(){ // data数据并未渲染到页面上
console.table([{
生命周期: "beforeMount",
解释说明: "data数据未渲染到页面上",
时间戳: new Date().getTime(),
data数据: this.number,
渲染数据: document.getElementById("mydomid").innerText,
挂载节点:this.$el
}])
},
mounted: function(){ // data数据已经渲染到页面上
console.table([{
生命周期: "mounted",
解释说明: "data数据已渲染到页面上",
时间戳: new Date().getTime(),
data数据: this.number,
渲染数据: document.getElementById("mydomid").innerText,
挂载节点:this.$el
}])
},
beforeDestroy: function(){ // 实例销毁之前执行
console.table([{
生命周期: "beforeDestroy",
解释说明: "实例销毁之前执行",
时间戳: new Date().getTime(),
data数据: this.number,
渲染数据: document.getElementById("mydomid").innerText,
挂载节点:this.$el
}])
},
destroyed: function(){ // 实例销毁之后执行
console.table([{
生命周期: "destroyed",
解释说明: "实例销毁之后执行",
时间戳: new Date().getTime(),
data数据: this.number,
渲染数据: document.getElementById("mydomid").innerText,
挂载节点: this.$el
}])
},
beforeUpdate: function(){ // 数据发生改变,还未渲染之前
console.table([{
生命周期: "beforeUpdate",
解释说明: "数据发生改变,还未渲染之前",
时间戳: new Date().getTime(),
data数据: this.number,
渲染数据: document.getElementById("mydomid").innerText,
挂载节点:this.$el
}])
},
updated: function(){ // 数据发生改变,重新渲染之后
console.table([{
生命周期: "updated",
解释说明: "数据发生改变,重新渲染之后",
时间戳: new Date().getTime(),
data数据: this.number,
渲染数据: document.getElementById("mydomid").innerText,
挂载节点:this.$el
}])
},
})
</script>
</body>
</html>
生命周期函数不是放在 methods
对象里,而是单独的放在 vue
的实例里面
vue 一共有11个生命周期函数,上面只讲解了8个生命周期函数,另外3个( activated
, deactivated
,errorCaptured
)参考官方文档API 选项 / 生命周期钩子
不要在选项 property 或回调上使用箭头函数,比如 created: () => console.log(this.a)
或 vm.$watch('a', newValue => this.myMethod())
。因为箭头函数并没有 this
,this
会作为变量一直向上级词法作用域查找,直至找到为止,经常导致 Uncaught TypeError: Cannot read property of undefined
或 Uncaught TypeError: this.myMethod is not a function
之类的错误。
3-3 Vue的模版语法
插值表达式 {{
开始,}}
结束
v-***="value"
,value
指的不在是某个值,而是 js
变量 或者 表达式
v-text=""
等于 插值表达式
v-html=""
类似插值表达式, 但是v-html=""
能解析标签内容,而插值表达式和 v-text
是不可以的
<div id="app">
<div v-text="mess + ' 表达式拼接'"></div>
<div v-html="mess + ' 表达式拼接'"></div>
<div v-html="messhtml + ' 表达式拼接'"></div>
{{mess + ' 表达式拼接'}}
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
mess:"hello world",
messhtml:"<h1>hello world</h1>",
}
})
</script>
3-4 计算属性,方法与侦听器
computed
计算属性 计算属性存在 缓冲机制
- 当计算的内容没有发生变化时,页面重新渲染的时候不会重新进行计算渲染该元素
- 当计算的内容发生变化时,页面重新渲染的时候会重新进行计算渲染该元素
<div id="app">
{{fullName}}
{{age}}
<hr>
<button @click="count">计算属性缓冲机制,当计算的内容没有发生变化时,页面重新渲染的时候不会重新进行计算渲染该元素/弹窗只会在运行的时候弹出1次</button>
<br><br>
<button @click="upName">计算属性缓冲机制,当计算的内容发生变化时,页面重新渲染的时候会重新进行计算渲染该元素/弹窗会在运行的时候再次弹出1次</button>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
firstName: "xiao",
lastName: "dongxier",
age: "28"
},
// 计算属性,内置缓存
computed: {
fullName: function(){
alert("计算了1次")
return this.firstName + " " + this.lastName
}
},
methods: {
count: function(){
this.age ++
},
upName: function(){
this.firstName += "xiao"
}
},
})
</script>
methods
方法 虽然改变的是
age
,但是只要页面重新渲染都会再次执行计算一次
相同的效果 计算属性 和 方法 都可以实现的情况下,计算属性 的性能更高
- 当计算的内容没有发生变化时,页面重新渲染的时候也会重新进行计算渲染该元素,性能方面不如 计算属性
<div id="app">
{{fullName()}}
{{age}}
<hr>
<button @click="count">方法,当计算的内容没有发生变化时,页面重新渲染的时候会重新进行计算渲染该元素/弹窗会在每次改变的时候弹出1次</button>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
firstName: "xiao",
lastName: "dongxier",
age: "28"
},
methods: {
fullName: function(){
alert("计算了1次")
return this.firstName + " " + this.lastName;
},
count: function(){
this.age ++
}
},
})
</script>
watch
侦听器 存在 缓冲机制,与 计算属性
computed
类似,如果同一个需求两种方法都能实现,那么优先使用 计算属性computed
,因为相比 侦听器watch
更简洁,性能更高
<div id="app">
{{fullName}} {{age}}
<hr>
<button @click="count">侦听器缓冲机制,当计算的内容没有发生变化时,页面重新渲染的时候不会重新进行计算渲染该元素/控制台只会在运行的时候打印1次</button>
<br><br>
<button @click="upName">侦听器缓冲机制,当计算的内容发生变化时,页面重新渲染的时候会重新进行计算渲染该元素/控制台会在运行的时候再打印1次</button>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
firstName: "xiao",
lastName: "dongxier",
fullName: "xiao dongxier",
age: "28"
},
watch: {
firstName: function() {
console.log("计算了1次")
this.fullName = this.firstName + " " + this.lastName
},
lastName: function() {
console.log("计算了1次")
this.fullName = this.firstName + " " + this.lastName
}
},
methods: {
count: function() {
this.age++
},
upName: function() {
this.firstName += "xiao"
}
}
})
</script>
- 模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。
3-5 计算属性的 getter 和 setter
计算属性
computed
上的get
方法通过其他值算一个新值,还可以通过set
方法 通过设置一个值,来改变相关联的值,改变相关联值的同时又会引起计算属性的相关计算,重新渲染页面上的内容
<div id="app">{{fullName}}</div>
<script>
var app = new Vue({
el: "#app",
data: {
firstName: "xiao",
lastName: "dongxier"
},
computed: {
fullName: {
get: function(){
return this.firstName + " " + this.lastName
},
set: function(val){
console.log(val)
var arr = val.split(" ");
this.firstName = arr[0]
this.lastName = arr[1]
}
}
}
})
3-6 Vue中的样式绑定
DOM 绑定样式
class
对象绑定
<div id="app">
<div @click="handleDivClick"
:class="{actived: isActived}"
>
hello world
</div>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
isActived: false
},
methods: {
handleDivClick: function(){
this.isActived = !this.isActived
}
},
})
</script>
class
数组绑定
动态的新增
class
或删除class
,实现页面效果的变更
<div id="app">
<div @click="handleDivClick"
:class="[activated, activatedOne]"
>
hello world
</div>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
activated: "",
activatedOne: "activated-one"
},
methods: {
handleDivClick: function(){
// if(this.activated === "activated") {
// this.activated = ""
// } else {
// this.activated = "activated"
// }
console.log(this.activated === "activated")
this.activated = (this.activated === "activated") ? "" : "activated"
}
}
})
</script>
style
改变样式 (内链)
对象方式
<div id="app">
<div @click="handleDivClick"
:style="styleObj"
>
hello world
</div>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
styleObj: {
color: '#000'
}
},
methods: {
handleDivClick: function(){
this.styleObj.color = (this.styleObj.color === "#000") ? "red" : "#000"
}
}
})
</script>
数组方式
由数组里的对象所决定,数组内可以添加多个对象
<div id="app">
<div @click="handleDivClick" :style="[styleObj, {fontSize: '30px'}]">
hello world
</div>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
styleObj: {
color: '#000'
}
},
methods: {
handleDivClick: function() {
this.styleObj.color = (this.styleObj.color === "#000") ? "red" : "#000"
}
}
})
</script>
3-7 Vue中的条件渲染
v-if
DOM节点移除或添加
v-if
和 v-else-if
和 v-else
同时使用的时候要紧挨着使用,否则会报错
<div v-if="show === 'a'">This is A</div>
<div v-else-if="show === 'b'">This is B</div>
<div v-else>This is others</div>
v-show
DOm节点一直存在,display:none/blcok
<div id="app">
<!-- 不存在dom节点/移除节点 -->
<div v-if="show === 'a'">This is A</div>
<div v-else-if="show === 'b'">This is B</div>
<div v-else>This is others</div>
<!-- 存在dom节点/display:none -->
<div v-show="show" >{{mess}}</div>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
show: "a",
mess: "hello world"
}
})
</script>
- 频繁的显示或者隐藏情况下
v-show
性能优于v-if
key
Vue
重新渲染页面时,会优先复用页面上已经存在的DOM
(内容不清空,如果输入了内容切换的时候,input
中的内容还是存在),例子如下
<div id="app">
<div v-if="show">
用户名:<input type="text">
</div>
<div v-else>
密码:<input type="text">
</div>
<button @click="btnClick">切换</button>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
show: true,
},
methods: {
btnClick: function() {
this.show = !this.show
}
},
})
</script>
通过添加 key
可以解决上面存在的问题
当给元素添加 key
的时候,Vue
认为是页面上唯一的一个元素,如果两个元素的 key
不一样,Vue
就不会尝试去复用该元素,就可以避免上面的情况发生
<div id="app">
<div v-if="show">
用户名:<input type="text" key="username">
</div>
<div v-else>
密码:<input type="text" key="password">
</div>
<button @click="btnClick">切换</button>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
show: true,
},
methods: {
btnClick: function() {
this.show = !this.show
}
},
})
</script>
- 因为
v-if
是一个指令,所以必须将它添加到一个元素上。但是如果想切换多个元素呢?此时可以把一个<template>
元素当做不可见的包裹元素,并在上面使用v-if
。最终的渲染结果将不包含<template>
元素。
<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
v-else
元素必须紧跟在带v-if
或者v-else-if
的元素的后面,否则它将不会被识别。- 类似于
v-else
,v-else-if
也必须紧跟在带v-if
或者v-else-if
的元素之后。 Vue
会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。这么做除了使Vue
变得非常快之外,还有其它一些好处。例如,如果你允许用户在不同的登录方式之间切换。<input>
不会被替换掉——仅仅是替换了它的placeholder
。
v-show
不支持 <template>
元素,也不支持 v-else
。
v-if vs v-show
v-if
是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。v-if
也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。- 相比之下,
v-show
就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。 - 一般来说,
v-if
有更高的切换开销,而v-show
有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用v-show
较好;如果在运行时条件很少改变,则使用v-if
较好。
v-if 与 v-for 一起使用
v-if
和 v-for
。请查阅风格指南以获取更多信息。
当 v-if
与 v-for
一起使用时,v-for
具有比 v-if
更高的优先级。请查阅列表渲染指南以获取详细信息。
3-8 Vue中的列表渲染
key
key
值唯一,最好不要用 index
下标作为 key
,正式项目一般后端会返回前端一个唯一的值
<div id="app">
<ul>
<li v-for="(item,index) of list"
:key="item.id">
{{item.name}} --- {{index}}
</li>
</ul>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
list: [
{
name: "xiao",
id: "00001"
},
{
name: "dong",
id: "00002"
},
{
name: "xi",
id: "00003"
},
{
name: "er",
id: "00004"
}
]
}
})
</script>
数组的循环
push / pop / shift / unshift / splice – 数组的截取/ sort – 对数组进行排序 / reverse -对数组进行取反
修改数组内容的时候, 不能通过下标的方式进行修改,这样操作打印 data
数据的时候,数据虽然添加成功了,但是不会渲染到页面上,只能通过 Vue
提供的几个变异方法来操作数组才能数据发生变化
数据改变的方法
slice("截取开始位置下标", "删除条数", "增加的内容")
或者 以上的其他的方法- 改变数组的引用
app.list = [
{
name: "xiao",
id: "00001"
},
{
name: "dong",
id: "00002"
},
{
name: "xi",
id: "00003"
},
{
name: "er",
id: "00004"
}
];
template
模版占位符
template
可以包裹一些元素,但在循环的过程中并不会渲染到页面上
<div id="app">
<template v-for="(item,index) of list"
:key="item.id">
<div>
{{item.name}} --- {{index}}
</div>
<span>
{{item.name}}
</span>
</template>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
list: [
{
name: "xiao",
id: "00001"
},
{
name: "dong",
id: "00002"
},
{
name: "xi",
id: "00003"
},
{
name: "er",
id: "00004"
}
]
}
})
</script>
对象的循环
key : 键名 ; index :当前项的索引,类似数组的下标
<div id="app">
<!-- key: 键值 index: 当前项的索引 -->
<div v-for="(item,key,index) of list">
{{item}}---{{key}} ---{{index}}
</div>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
list: {
name: "xiaodonxgier",
age: "18",
gender: "male",
salary: "secret"
}
}
})
数据改变的方法
直接加值是无法渲然到页面上的
- 改变引用
app.list = {
name: "xiaodongxier",
age: "18",
gender: "male",
salary: "secret",
address: "Beijing"
}
- 你也可以用
of
替代in
作为分隔符,因为它更接近JavaScript
迭代器的语法:
<div v-for="item in items"></div>
<div v-for="item of items"></div>
Object.keys()
的结果遍历,但是不能保证它的结果在不同的 JavaScript 引擎下都一致。
v-for
的 key
。请用字符串或数值类型的值。
- 由于 JavaScript 的限制,Vue 不能检测数组和对象的变化。深入响应式原理中有相关的讨论。
v-for
也可以接受整数。在这种情况下,它会把模板重复对应次数。
<div>
<span v-for="n in 10">{{ n }} </span>
</div>
输出结果
1 2 3 4 5 6 7 8 9 10
- 2.2.0+ 的版本里,当在组件上使用
v-for
时,key
现在是必须的。
3-9 Vue中的set方法
即使
Vue
全局的一个方法,也是Vue
实例上的方法
set
方法
对象的 <div id="app">
<!-- key: 键值 index: 对象的位置 -->
<div v-for="(item,key,index) of list">
{{item}}---{{key}} ---{{index}}
</div>
<span v-for="n in 10">{{ n }} </span>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
list: {
name: "xiaodonxgier",
age: "18",
gender: "male",
salary: "secret"
}
}
})
</script>
Vue.set("实例数据", "新增的属性键", "新增的数据值")
通过 set
方法往上面案例添加数据的写法为:
全局方法:
Vue.set(app.list, "address", "Beijing")
实例方法:
app.$set(app.list, "address", "Beijing")
set
方法
数组的 <div id="app">
<!-- key: 键值 index: 对象的位置 -->
<div v-for="(item,key,index) of list">
{{item}}---{{key}} ---{{index}}
</div>
<span v-for="n in 10">{{ n }} </span>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
list: [1,2,3,4,5,6,7,8]
}
})
</script>
Vue.set("实例数据", "需要改变的数组下标", "更新的内容")
通过 set
方法向上面案例中的数据 2
变成 22
的方法为:
全局方法:
Vue.set(app.list, 1, 22)
实例方法:
app.$set(app.list, 1, 22)
3-10 (新)Vue中的事件绑定
<div id="app">
<button @click="handleBtnClcik">Button</button>
<button @click="handleBtnClcik()">Button()</button>
<button @click="handleBtnClcik($event)">Button($event)</button>
<button @click="handleBtnClcik($event,1,2,3)">Button($event)优势/可以传参</button>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
list: [1,2,3,4,5,6,7,8]
},
methods: {
handleBtnClcik:function(e,one,teo,three){
console.log(e,one,teo,three)
}
},
})
</script>
事件修饰符
prevent
阻止跳转
stop
阻止向外冒泡
self
self要求,click事件只有在e.target = e.currenTaget 的时候才会执行/触发事件和绑定事件的元素相等的时候才会执行
once
once修饰符/事件执行一次之后自动解绑/事件只执行一次
capture
加了Capture修饰符/默认事件冒泡(内->外)被改变未捕获(外->内)/先执行父事件在执行子事件
<div id="app">
<form action="/abc">
<input type="submit">
<span>未添加事件</span>
</form>
<form action="/abc" @click="handleBtnClcik">
<input type="submit">
<span>添加了阻止事件</span>
</form>
<form action="/abc" @click.prevent>
<input type="submit">
<span>添加了事件修复</span>
</form>
<form action="/abc" @click.prevent="handleBtnClcik">
<input type="submit">
<span>添加了事件修复/后面也可以添加其他事件不影响</span>
</form>
<form action="/abc" @click.stop>
<input type="submit">
<span>stop修饰符/阻止向外冒泡</span>
</form>
<form action="/abc" @click.self>
<div @click="handleSelfClick">
父元素
<div>
子元素
</div>
</div>
<span>未加self修饰符/点击父元素和子元素都能触发事件</span>
</form>
<form action="/abc" @click.self>
<div @click.self="handleSelfClick">
父元素
<div>
子元素
</div>
</div>
<span>加了self修饰符/只有点击父元素才都能触发事件,点击子元素是不会触发的</span>
<br>
<span>self要求,click事件只有在e.target = e.currenTaget 的时候才会执行/触发事件和绑定事件的元素相等的时候才会执行</span>
</form>
<form action="/abc" @click.self>
<div @click.once="handleOnceClick">
<div>点我触发事件</div>
</div>
<span>once修饰符/事件执行一次之后自动解绑/事件只执行一次</span>
</form>
<form action="/abc" @click.self>
<div @click="handleCaptureClickA">
<div @click="handleCaptureClickB">点我触发事件</div>
</div>
<span>未加Capture修饰符/默认事件冒泡(内->外)/先执行子事件在执行父事件</span>
</form>
<form action="/abc" @click.self>
<div @click.capture="handleCaptureClickA">
<div @click.capture="handleCaptureClickB">点我触发事件</div>
</div>
<span>加了Capture修饰符/默认事件冒泡(内->外)被改变未捕获(外->内)/先执行父事件在执行子事件</span>
</form>
</div>
<script>
var app = new Vue({
el: "#app",
methods: {
handleBtnClcik: function (e,) {
e.preventDefault();
},
handleSelfClick: function(){
alert("handleSelfClick")
},
handleOnceClick: function(){
alert("handleOnceClick")
},
handleCaptureClickA: function(){
alert("父元素事件/外")
},
handleCaptureClickB: function(){
alert("子元素事件/内")
}
},
})
</script>
按键修饰符
enter/tab/delete/esc/shift/alt/….
当摁下哪个键的时候执行
<div id="app">
<input type="text" @keydown="handleKeyDown">:输入的实时弹出内容
<br><br>
<input type="text" @keydown.enter="handleKeyDown">:回车/enter 的时候弹出结果
<br><br>
<input type="text" @keydown.delete="handleKeyDown">:delete的时候弹出结果
<br><br>
<input type="text" @keydown.esc="handleKeyDown">:esc的时候弹出结果
<br><br>
<input type="text" @keydown.shift="handleKeyDown">:shift的时候弹出结果
<br><br>
<input type="text" @keydown.alt="handleKeyDown">:alt的时候弹出结果
<br><br>
<input type="text" @keydown.tab="handleKeyDown">:tab的时候弹出结果
</div>
<script>
var app = new Vue({
el: "#app",
methods: {
handleKeyDown: function (e) {
// e.target 指的是 input dom框这个节点,通过原生value可以把输入框的内容弹出来
alert(e.target.value)
}
},
})
</script>
系统修饰符
ctrl/alt/shift/meta
需要同时摁住才能执行
鼠标修饰符
right/left/middle
- 有一些按键 (
.esc
以及所有的方向键) 在 IE9 中有不同的key
值, 如果你想支持 IE9,这些内置的别名应该是首选。 - 注意:在 Mac 系统键盘上,meta 对应 command 键 (⌘)。在 Windows 系统键盘 meta 对应 Windows 徽标键 (⊞)。在 Sun 操作系统键盘上,meta 对应实心宝石键 (◆)。在其他特定键盘上,尤其在 MIT 和 Lisp 机器的键盘、以及其后继产品,比如 Knight 键盘、space-cadet 键盘,meta 被标记为“META”。在 Symbolics 键盘上,meta 被标记为“META”或者“Meta”。
- 请注意修饰键与常规按键不同,在和
keyup
事件一起用时,事件触发时修饰键必须处于按下状态。换句话说,只有在按住ctrl
的情况下释放其它按键,才能触发keyup.ctrl
。而单单释放ctrl
也不会触发事件。如果你想要这样的行为,请为ctrl
换用keyCode
:keyup.17
。
3-11 (新)Vue中的表单绑定
<div id="app">
<div @click.right="handleRightClick">handleRightClick</div>
<div @click.left="handleLeftClick">handleLeftClick</div>
<div @click.middle="handleMiddleClick">handleMiddleClick</div>
</div>
<script>
var app = new Vue({
el: "#app",
methods: {
handleRightClick: function () {
console.log("handleRightClick")
},
handleLeftClick: function () {
console.log("handleRightClick")
},
handleMiddleClick: function () {
console.log("handleRightClick")
}
},
})
3-12 (新)章节小节
- 实例
- 模版语法
- v-if
- v-text
- v-html
- v-for
- v-if
- v-else
- v-else-if
- v-show
- v-bind
- v-on
- 计算属性
- 避免代码冗余
- 样式
- class
- style
- 对象绑定
- 数组绑定
- 条件渲染
- v-if
- v-else
- v-else-if
- template 占位符
- 列表渲染
- 数组
- value值
- index值(索引值)
- 对象
- value值
- key值(键值)
- index值(索引值)
- 数组
- 事件处理
- 事件绑定
-
事件修饰符
- 表单绑定
- v-model
- textarea
- input
- option
- v-model
3-13 【讨论题】你对前端中的面向对象有怎样的了解?
什么是面向对象
Object
:一切皆对象- 对象并不是计算机领域凭空造出来的概念,它是顺着人类思维模式产生的一种抽象
- 人的成长过程中先认识到对象,然后才有过程、值的概念【一个苹果(实例)可以吃——所有苹果(类)可以吃——3(值)个苹果和3个梨的关系】
- 所以
面对对象编程
是更接近人类思维的一种编程范式 - 最成功的流派使用类的方式来描述对象,如
JAVA
JavaScript
选择了较冷门的原型【因为一些公司政治原因,JavaScript 推出之时受管理层之命被要求模仿 Java,JavaScript 创始人 Brendan Eich 在“原型运行时”的基础上引入了 new、this 等语言特性,使之“看起来更像 Java”】ES6
之前,很多人试图将JavaScript
变得更像基于类的语言,进而产生了很多所谓的“框架”,比如PrototypeJS、Dojo
【事实上,它们成为了某种 JavaScript 的古怪方言,甚至产生了一系列互不相容的社群,显然这样做的收益是远远小于损失的】- 从运行角度谈论对象:任何代码执行都必定绕不开运行时的对象模型【运行时类的概念都是被弱化的】
JavaScript
面向对象编程:命名空间
是一个容器,它允许开发人员在一个独特的,特定于应用程序的名称下捆绑所有的功能。JavaScript
中,命名空间只是另一个包含方法、属性、对象的对象- 注意:需要认识到重要的一点是:与其他面向对象编程语言不同的是,
Javascript
中的普通对象和命名空间在语言层面上没有区别。这点可能会让JavaScript
初学者感到迷惑。 - 创造的
JavaScript
命名空间背后的想法很简单:一个全局对象被创建,所有的变量,方法和功能成为该对象的属性。使用命名空间也最大程度地减少应用程序的名称冲突的可能性。 JavaScript
有包括在其核心的几个对象,例如,Math,Object,Array和String
对象
JavaScript
对象的特征
- 对象具有唯一标识性:即使是完全相同的两个对象,也并非是同一个对象
let o1 = {a: 1}
let o2 = {a: 1}
console.log(o1 == o2) //false
- 对象具有状态:同一对象可能处于不同的状态之下
- 对象具有行为:对象的状态可能因为它的行为而产生变迁
a) 状态和行为在不同的语言中使用不同的术语来描述
b) java中称为属性(状态)和方法(行为)
c) javascript中将状态和行为统一抽象为“属性”
d) javascript中函数是一种特殊的对象
let age = 20
let o = {
name: 'Lily',
sayHi(){
console.log('Hi')
}
}
- 除了上述3个基本特征外,
JavaScript
的独特之处:高度的动态性,运行时改变对象的状态和行为(属性)的能力 JavaScript
的属性比别的语言更复杂,它提供了数据属性和访问属性(getter/setter
)JavaScript
对象的两类属性
6.1Vue项目预热 – 环境配置
nodejs
LTS – 长期维护版
Current – 最新版
版本检测 node -v
创建vue项目
全局安装 vue-cli
$ npm install -g vue-cli
创建项目
$ vue init <template-name> <project-name>
$ vue init webpack mkw-vue-qnew-develop
项目初始化
$ vue init webpack my-project
6.2 (新)Vue项目预热 – 项目环境准备
卸载安装的新的版本
npm uninstall @vue/cli -g
6.3 项目代码结构
- README – 项目的说明文件
- package – 第三方模块依赖包
- package-lock – package的一个锁文件,确定安装第三方包的版本,保证团队编程的统一
- LICENSE – 开源协议声明
- index.html – 默认的项目首页模版文件
- .postcssrc.js – post css配置项
- .gitignore – git提交过滤,不想提交到仓库的文件可以进行配置
- .eslintrc – 代码规范检测
- .eslintignore – 文件不想被 eslintrc 进行检测可以配置到里面,写的不标准也不会进行提示
- .editorconfig – 关于编辑器的一些配置,方便统一编程
- .babelrc – 语法转换,转换成浏览器可以正常编译执行的代码
- static – 静态资源
- .gitkeep –
- src – 整个项目的源代码
- assets – 静态资源
- components – 组件
- router/index.js – 路由
- App.vue – 最原始的根组建
- main.js – 整个项目的入口文件
- node_modules – 项目依赖的第三方包
- config – 项目的配置文件
- dev.env.js – 开发环境配置
- index.js.js – 基础的配置信息
- prod.env.js – 线上环境配置
- build – webpack配置项/vue-cli自动构建的/无需修改
- build.js –
- check-versions.js –
- utils.js –
- vue-loader.conf.js –
- webpack.base.conf.js – 基础的webpack配置项
- webpack.dev.conf.js – 开发环境的webpack配置项
- webpack.prod.conf.js – 线上环境的webpack配置项
6.4 单文件组件与Vue中的路由
components: { App }
es6的写法,同下面写法
components: {
App : App
}
import App from ‘./App’
默认去找App.js/App.json/App.vue等文件
以.vue结尾的文件称之为单文件组件
单文件组件
路由
路由:根据网址的不同,返回不同的内容给用户
<router-view/>
当前路由地址所对应的内容
注意
style
后面要回车一行export default
中name
的值要是单引号
6.4 单文件组件与Vue中的路由
多页面应用
页面跳转-》返回HTML
优点:首屏时间快/SEO优化号
缺点:页面切换慢
单页面应用
vue里一般不用
a
标签做页面跳转
template
里面像外暴露只能暴露一个跟标签
搜索引擎只认识html内容不认识js内容,所以seo差
页面跳转-》js渲染
优点:页面切换快
缺点:首屏时间稍慢,SEO差
后期会学到服务器端渲染等可内容以解决但页面应用目前存在的问题
6.5 项目代码初始化
viewport
viewport 的 meta 标签进行配置,移动端禁止手指缩放 ios safari浏览器目测不起作用
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
reset
引入reset.css文件
import './assets/styles/reset.css'
,重置css默认样式进行浏览器之间样式的统一
@charset "utf-8";html{touch-action: manipulation;background-color:#fff;color:#000;font-size:12px}
body,ul,ol,dl,dd,h1,h2,h3,h4,h5,h6,figure,form,fieldset,legend,input,textarea,button,p,blockquote,th,td,pre,xmp{margin:0;padding:0}
body,input,textarea,button,select,pre,xmp,tt,code,kbd,samp{line-height:1.5;font-family:tahoma,arial,"Hiragino Sans GB",simsun,sans-serif}
h1,h2,h3,h4,h5,h6,small,big,input,textarea,button,select{font-size:100%}
h1,h2,h3,h4,h5,h6{font-family:tahoma,arial,"Hiragino Sans GB","微软雅黑",simsun,sans-serif}
h1,h2,h3,h4,h5,h6,b,strong{font-weight:normal}
address,cite,dfn,em,i,optgroup,var{font-style:normal}
table{border-collapse:collapse;border-spacing:0;text-align:left}
caption,th{text-align:inherit}
ul,ol,menu{list-style:none}
fieldset,img{border:0}
img,object,input,textarea,button,select{vertical-align:middle}
article,aside,footer,header,section,nav,figure,figcaption,hgroup,details,menu{display:block}
audio,canvas,video{display:inline-block;*display:inline;*zoom:1}
blockquote:before,blockquote:after,q:before,q:after{content:"\0020"}
textarea{overflow:auto;resize:vertical}
input,textarea,button,select,a{outline:0 none;border: none;}
button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}
mark{background-color:transparent}
a,ins,s,u,del{text-decoration:none}
sup,sub{vertical-align:baseline}
html {overflow-x: hidden;height: 100%;font-size: 50px;-webkit-tap-highlight-color: transparent;}
body {font-family: Arial, "Microsoft Yahei", "Helvetica Neue", Helvetica, sans-serif;color: #333;font-size: .28em;line-height: 1;-webkit-text-size-adjust: none;}
hr {height: .02rem;margin: .1rem 0;border: medium none;border-top: .02rem solid #cacaca;}
a {color: #25a4bb;text-decoration: none;}
border
引入border.css文件
import './assets/styles/border.css'
,解决移动端1px问题
@charset "utf-8";
.border,
.border-top,
.border-right,
.border-bottom,
.border-left,
.border-topbottom,
.border-rightleft,
.border-topleft,
.border-rightbottom,
.border-topright,
.border-bottomleft {
position: relative;
}
.border::before,
.border-top::before,
.border-right::before,
.border-bottom::before,
.border-left::before,
.border-topbottom::before,
.border-topbottom::after,
.border-rightleft::before,
.border-rightleft::after,
.border-topleft::before,
.border-topleft::after,
.border-rightbottom::before,
.border-rightbottom::after,
.border-topright::before,
.border-topright::after,
.border-bottomleft::before,
.border-bottomleft::after {
content: "\0020";
overflow: hidden;
position: absolute;
}
/* border
* 因,边框是由伪元素区域遮盖在父级
* 故,子级若有交互,需要对子级设置
* 定位 及 z轴
*/
.border::before {
box-sizing: border-box;
top: 0;
left: 0;
height: 100%;
width: 100%;
border: 1px solid #eaeaea;
transform-origin: 0 0;
}
.border-top::before,
.border-bottom::before,
.border-topbottom::before,
.border-topbottom::after,
.border-topleft::before,
.border-rightbottom::after,
.border-topright::before,
.border-bottomleft::before {
left: 0;
width: 100%;
height: 1px;
}
.border-right::before,
.border-left::before,
.border-rightleft::before,
.border-rightleft::after,
.border-topleft::after,
.border-rightbottom::before,
.border-topright::after,
.border-bottomleft::after {
top: 0;
width: 1px;
height: 100%;
}
.border-top::before,
.border-topbottom::before,
.border-topleft::before,
.border-topright::before {
border-top: 1px solid #eaeaea;
transform-origin: 0 0;
}
.border-right::before,
.border-rightbottom::before,
.border-rightleft::before,
.border-topright::after {
border-right: 1px solid #eaeaea;
transform-origin: 100% 0;
}
.border-bottom::before,
.border-topbottom::after,
.border-rightbottom::after,
.border-bottomleft::before {
border-bottom: 1px solid #eaeaea;
transform-origin: 0 100%;
}
.border-left::before,
.border-topleft::after,
.border-rightleft::after,
.border-bottomleft::after {
border-left: 1px solid #eaeaea;
transform-origin: 0 0;
}
.border-top::before,
.border-topbottom::before,
.border-topleft::before,
.border-topright::before {
top: 0;
}
.border-right::before,
.border-rightleft::after,
.border-rightbottom::before,
.border-topright::after {
right: 0;
}
.border-bottom::before,
.border-topbottom::after,
.border-rightbottom::after,
.border-bottomleft::after {
bottom: 0;
}
.border-left::before,
.border-rightleft::before,
.border-topleft::after,
.border-bottomleft::before {
left: 0;
}
@media (max--moz-device-pixel-ratio: 1.49), (-webkit-max-device-pixel-ratio: 1.49), (max-device-pixel-ratio: 1.49), (max-resolution: 143dpi), (max-resolution: 1.49dppx) {
/* 默认值,无需重置 */
}
@media (min--moz-device-pixel-ratio: 1.5) and (max--moz-device-pixel-ratio: 2.49), (-webkit-min-device-pixel-ratio: 1.5) and (-webkit-max-device-pixel-ratio: 2.49), (min-device-pixel-ratio: 1.5) and (max-device-pixel-ratio: 2.49), (min-resolution: 144dpi) and (max-resolution: 239dpi), (min-resolution: 1.5dppx) and (max-resolution: 2.49dppx) {
.border::before {
width: 200%;
height: 200%;
transform: scale(.5);
}
.border-top::before,
.border-bottom::before,
.border-topbottom::before,
.border-topbottom::after,
.border-topleft::before,
.border-rightbottom::after,
.border-topright::before,
.border-bottomleft::before {
transform: scaleY(.5);
}
.border-right::before,
.border-left::before,
.border-rightleft::before,
.border-rightleft::after,
.border-topleft::after,
.border-rightbottom::before,
.border-topright::after,
.border-bottomleft::after {
transform: scaleX(.5);
}
}
@media (min--moz-device-pixel-ratio: 2.5), (-webkit-min-device-pixel-ratio: 2.5), (min-device-pixel-ratio: 2.5), (min-resolution: 240dpi), (min-resolution: 2.5dppx) {
.border::before {
width: 300%;
height: 300%;
transform: scale(.33333);
}
.border-top::before,
.border-bottom::before,
.border-topbottom::before,
.border-topbottom::after,
.border-topleft::before,
.border-rightbottom::after,
.border-topright::before,
.border-bottomleft::before {
transform: scaleY(.33333);
}
.border-right::before,
.border-left::before,
.border-rightleft::before,
.border-rightleft::after,
.border-topleft::after,
.border-rightbottom::before,
.border-topright::after,
.border-bottomleft::after {
transform: scaleX(.33333);
}
}
fastclick
300ms click点击延迟的问题,安装fastclick
sudo npm i fastclick --save
进行解决,安装到依赖之中,--save
不管是开发环境,还是打包成线上版本的代码,都需要使用fastclick
安装存储在
package.json
中的dependencies
里面
引入fastClick
import fastClick from 'fastclick'
,使用fastClickfastClick.attach(document.body)
iconfont
创建项目
无用代码删除
代码提交线上
git add . //本地修改提交到git缓存区
git commit -m "注释" //本地缓冲区提交的本地仓库
git push //本地代码提交到线上
7-1 Vue项目首页 – header区域开发
宽带750px
按照iPhone6的2倍图
stylus 安装
npm i stylus --save
https://www.stylus-lang.cn
CSS的预处理框架,即将stylus转换为css使用, 可以使用变量
stylus-loader 安装
npm i stylus-loader --save
让webpack理解stylus
header 位置开发
局部组建使用
组建下面声明局部组建
export default {
name: 'Home',
components: {
HomeHeader: HomeHeader
}
}
与下面相同
export default {
name: 'Home',
components: {
HomeHeader
}
}
home-header
== HomeHeader
scoped css样式只对当前组建生效
1rem = html font-size = 50px
86/100 = .86rem
lang="stylus"
报错
报错截图
报错版本
解决方法:https://segmentfault.com/q/1010000025218065 ,下面的评论
stylus-loader 版本太高 把版本降到3.0.2就解决了最后贴一个GitHub issues的地址https://github.com/vuejs/vue-cli/issues/5947
卸载命令 npm uninstall stylus-loader
安装3.0.2 版本
安装指定版本命令 npm i stylus-loader@3.0.2 --save
其他解决方法
方法1
- 不卸载之前的高版本
- 运行命令
npm i stylus-loader@3.0.2 --save
方法2
- 先在package.json里修改好指定版本号,然后输入
- 运行命令
npm update stylus-loader@3.0.2
知识点
--save和--save-dev区别
一句话:--save-dev是你开发时依赖的东西,--save是发布后还依赖的东西.
注释事项
- 代码缩减问题
- 代码一行结束后面不能跟空格
header 位置开发
lang="stylus"
css使用stylus语法
scoped css样式只对当前组建生效
iconfont的使用和代码优化
iconfont引入
import './assets/styles/iconfont.css'
css值放到一个变量里面 – 方便后期视觉主题维护 -提高可维护性
样式里面引入样式 ,css引用需要使用 @import
@import '../../../assets/styles/varibles.styl';
@
代表 src
这个目录,但是在css
引用其他的css
使用的时候需要前面加~
@import '~@/assets/styles/varibles.styl';
styles 文件夹路径使用比较高,起一个别名代替,类似上面的 @,使代码变得更加精简
找到build/webpack.base.conf.js
这个文件(34行)
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
}
},
根据上面的写法可以为styles
路径新增一个别名
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
'styles': resolve('src/assets/styles'),
}
},
修改后会有报错提示
这是因为项目修改配置项,需要重启下服务器