一 vue脚手架2.0
安装
npm install vue-cli -g
查阅一下脚手架可支持的模板
vue list
可以查到 template-name
使用脚手架生成项目(以下命令得运行在项目的包裹目录下)
vue init webpack projectName
启动项目
1 进入到项目目录
2 启动开发环境:npm run dev 或者 npm start(开发环境)
3 启动生产环境:npm run bulid(生产打包)
4 自动启动chrom:在dev中加入: --open chrom
vue2.0脚手架的问题
没有办法让vue的开发者脱离对webpack的学习
不会webpack就不能学vue
比如项目中药使用eslint的强制约束,我们需要改动config目录下的showEslintErrorsInOverlay这个配置,而且在不同的webpack环境中 eslint的版本不一样 配置也会不一样!
我们急需vue推出一个脚手架 让vue的开发者远离webpack的配置!!! @vue/cli!!
vue.js完整版
new Vue({
el:"#app",
template: "<v-damu></v-damu>",
components:{
"v-damu":{
template:"<strong>v-damu</strong>"
}
}
})
// vue.runtime.js压缩版运行时的写法
new Vue({
el:"#app",
render(h){return h("v-damu")},
components:{
"v-damu":{
render(h){return h("strong","v-damu")},
}
}
})
// h: 创建dom节点(参数是节点名字符串) 也 直接创建组件!!!!(参数是组件对象)
new Vue({
el:"#app",
render(h){
return h({
name:"v-damu",
render(h){return h("strong","v-damu")},
})
},
})
二 webpack找包的机制
https://webpack.docschina.org/concepts/module-resolution
import App from "./App";
经过vue-loader的处理 我们拿到的就是app组件的配置对象
import Vue from "vue";
webpack找到的到底是一个什么样的文件?
webpack import的规则?
import Vue from "vue";
配别名的情况: "vue"是一个别名!!
直接找别名指定的文件
沒有配别名的情况: "vue"是一个包!!
1. 根据resolve.modules这个配置去指定的目录中查找vue这个包
2. 找包的描述文件package.json
3. 根据resolve.mainFields 去package.json找对应的字段
4. 如果以上步骤没有找到对应的js文件 则按照resolve.mainFiles字段指定的文件名 去找对应的文件
5. 文件的扩展名 由resolve.extensions来决定
6. 如果以上步骤都没有成功 那么webpack就找不到对应的包!
vue-loader .vue文件
三 配别名:webpack.base.conf.js
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
resolve: {
alias: {
"components":resolve('src/components')
}
}
scoped使用:css作用域
四 todolist案例
将静态页面拆分成静态组件
① 注意子组件的引入
② 静态资源一定要放在stasic中 或者 将静态资源作为模块引入;建议放在stasic,通过index.html的标签中引入
初始化静态页面
app.vue
<todo-list :list="list"></todo-list>
data(){
return {
list:[
{id:0,checked:true,name:"张三"},
{id:1,checked:true,name:"李四"},
{id:2,checked:true,name:"王五"},
{id:3,checked:true,name:"赵六"}
]
}
在list中通过props进行接参 因为list为数组,所以需要进循环遍历
<todo-item v-for="item in list" :key="item.id" :item="item"></todo-item>
props:{
list:Array //prop限制为数组类型
},
将静态数据放入到item中,可以在页面中呈现出来,通过props来接收值。
<span>{{item.name}}</span>
props:{
item:Object //prop限制为对象类型 因为{id:0,checked:true,name:"张三"}
},
添加todo数据
header .vue 进行事件的双向绑定,在输入框中输入内容,当点击回车的时候增加数据
<input type="text" placeholder="请输入你的任务名称,按回车键确认"
v-model.trim="name" @keydown.13="addTask"/>
let id = 4
data(){
return {
name:""
}
},
// 当按回车的时候会触发事件,增加数据
methods:{
addTask() {
// 1 获取用户输入,构建一个任务对象,塞到list中
let name = this.name;
let id = id
let task = {
id:id++,
checked:false,
name
}
// 2 实现list到app的子向父的数据传递
this.$emit("addTask",task)
// 3 清空输入框
this.name = ""
}
}
app.vue
methods:{
// 输入框中增加的是来自数组list中的数据,在前面增加数据
addTask(task) {
this.list.unshift(task)
},
}
子组件被选中传递数据
v-model=“item.checked” 脏数据 直接修改了子组件的内容,当list中数据较多的时候后期代码不易维护,因此用总线机制进行非父子间的传递,手动进行选中和改变
item.vue
<input type="checkbox" :checked="item.checked" @change="changeFn(item.id,$event)"/>
methods:{
changeFn(id,ev) {
console.log(ev.target) //<input data-v-79668b5a type ="checkbox">
//todo-item 将数据传给app(非父子)--总线机制
let checked = ev.target.checked
this.$bus.$emit("checked",id,checked) //将id,checked进行发布,在app中进行订阅查收
}
}
或者通过计算属性进行数据的传送和接收
computed:{
checked:{
//得到并返回被选中的值
get() {return this.item.checked},
//将得到的被选中的值进行弹射发布
set(checked) {
this.$bus.$emit("checked",this.item.id,checked)
}
}
}
在main.js入口文件中定义总线机制
Vue.protype.$bus = new Vue()
在app.vue中进行数据的订阅查收
methods:{ // 接收了$emit弹射的id,checked,根据id修改list中的数据
checked(id,checked) {
this.list.forEach((item)=> {
if(item.id === id) {
item.checked = checked
}
})
}
}
// 订阅查收在生命周期的mounted中进行 接收了$emit弹射的id,checked
mounted(){
this.$bus.$on("checked",this.checked)
},
注意:
① v-model.lazy不能和input.change一起使用,不能跟js认识的数据类型,一定跟类似data这种管理的数据
② v-model脏数据:
v-model=“非本组件的数据”,因为v-model是双向绑定,跟非本组件的数据语法上市没有问题的,违背了数据传递的规则:子组件直接修改了父组件的数据
解决:将v-model读取值和修改值得能力使用其他方案代替。
v-model读取值能力,可使用v-bind:value=“非本组件的数据”
v-model读取值能力,可使用v-model=“计算属性” 计算属性的get方法返回非组件的值来代替
v-model修改值能力,可以使用change事件+子向父数据传递来代替
v-model修改值能力,计算属性set方法+子向父数据传递来代替
删除todo数据
item:去除button中的display:none,加hover
li:hover {
background-color: pink;
}
li:hover > button {
display: block
}
非父子传递:总线机制 item
<button class="btn btn-danger" @click="delTask(item.id)">删除</button>
methods:{
// 删除item中的内容
deleteTask(id) {
this.$bus.$emit("deleteTask",id)
}
}
app.vue列表删除,在mounted中注册on事件,根据id删除list中数据
mounted(){
// item上的列表功能
this.$bus.$on("checked",this.checked)
// item上的列表删除功能
this.$bus.$on("delTask",this.delTask)
},
methods:{
delTask(id) {
this.list = this.list.filter(item=> item.id !== id)
}
}
需求扩展
item.vue 加–每个li给伪元素,用class和数据做绑定
.done {
position: relative;
}
.done:after {
content:'';
display: block;
height:3px;
background-color:red;
width: 80%;
position: absolute;
left: -55px;
right:0;
top: 0;
bottom: 0;
margin: auto;
}
<li :class="{done:checked}"></li>
完成footer功能
footer样式调整
<span>
<span>
<strong>已完成</strong>
<strong style="font-size:20px;color:green">{{doneNum}}</strong>
</span>
<span>/</span>
<span>
<strong>全部</strong>
<strong style="font-size:20px;color:red">{{list.length}}</strong>
</span>
</span>
已完成:传list 用props接收,已完成用计算属性(有依赖关系,且复杂)
<todo-footer :list="list"></todo-footer>
computed:{
// 已完成数量
doneNum() {
// 基础写法
// let num = 0;
// this.list.forEach(item => {
// if(item.checked) {
// num++
// }
// })
// return num
// 累加器reduce
return this.list.reduce((adder,item) =>{
// 简易写法
return item.checked ? adder+1 : adder
// if(item.checked) {
// return adder+1
// } else {
// return adder
// }
},0)
}
}
}
全选:v-model(计算属性),考虑不选的时候
<input type="checkbox" v-model="checkAll"/>
computed:{
//全选功能
checkAll:{
get(){
console.log("checkAll ,get")
//全选按钮选中的逻辑: 代办任务的数量等于已完成的数量 且 他们都不等于0
return( (this.list.length === this.doneNum) && (this.list.length!==0 && this.doneNum!==0) )
},
set(checked){
// checked: 当前全选按钮最新的状态
this.$emit("checkAll",checked)
}
}
}
}
app.vue:全选功能 用事件的方法(总线机制也可以)
<todo-footer :list="list" @checkAll="checkAll"></todo-footer>
methods:{
//全选功能
checkAll(checked){
this.list.forEach(item=>item.checked=checked)
},
}
footer:全部按钮禁用:input有个属性disabled 样式修改为:not-allow
<input type="checkbox" v-model="checkAll" :disabled="disabled"/>
input[disabled]{
cursor: not-allowed;
}
computed:{
//全选按钮的禁用
disabled(){
//当没有数据的时候,已完成和已选中的等于0不能被全选
return this.list.length===0 && this.doneNum===0
}
}
清除已完成任务
footer点击事件 触发app的清除任务
<button class="btn btn-danger" @clear="clear">清除已完成任务</button>
methods:{
clear() {
this.$emit("clear")
}
},
app.vue 清除任务:过滤操作
<todo-footer :list="list" @checkAll="checkAll" @clear="clear" ></todo-footer>
methods:{
//清除已完成任务
clear(){
this.list = this.list.filter( item => !item.checked)
}
}
收尾
全部用总线机制传值,利于维护,且美观
mounted() {
//item上的列表选中功能
this.$bus.$on("checked",this.checked);
//item上的列表删除功能
this.$bus.$on("delTask",this.delTask);
//往list中新增数据
this.$bus.$on("addTask",this.addTask);
//全选功能
this.$bus.$on("checkAll",this.checkAll);
//清除已完成任务
this.$bus.$on("clear",this.clear);
},
本地存储
app.vue:watch监听list到本地:属性名(变量名)
写:将newList存到localstorage中(存json字符串)
watch:{
//默认是浅监听
list:{
handler(newList){
//将newList存到localStorage中 (存json字符串!)
localStorage.setItem("todo-list-data",JSON.stringify(newList))
},
deep:true //开启深度监听
}
}
读:在生命周期的create中 读取数据,并赋值给list
created() {
//将储存中的数据读出来 赋值给list
const list = localStorage.getItem("todo-list-data")
this.list = list ? JSON.parse(list) : []
}
header.vue 修改id
data(){
let id = localStorage.getItem("todo-list-id")
id = id?JSON.parse(id) :0
return {
name:"",
id,
}
methods:{
//4 将id存储起来
localStorage.setItem("todo-list-id",JSON.stringify(this.id))
}