一、scoped样式属性(局部范围样式)及lang=" "的运用(默认情况下自动选择css,也不会出现lang=""让你选择语言的提示)
scoped样式属性
如果我们给两个子组件中添加的样式类名相同就会出现类名冲突的问题, vue显然页面的时候会最终把最后在app.vue中组测的组件的类名中的样式, 渲染出来!
例如:
LqjSchool.vue:
代码语言:javascript复制 <template>
<div class="School">
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
</div>
</template>
...
<style>
.School{
background: rgb(23, 190, 241);
}
</style>
StudentLqj.vue:
代码语言:javascript复制 <template>
<div class="School">
<h2>学生姓名:{{name}}</h2>
<h2>学生性别:{{msg}}</h2>
</div>
</template>
...
<style>
.School{
background: rgb(248, 120, 0);
}
</style>
App.vue:
代码语言:javascript复制import LqjSchool from './components/LqjSchool.vue'
import StudentLqj from './components/StudentLqj.vue'
注意:LqjSchool.vue中School的背景色为天蓝色 StudentLqj.vue中School的背景色为橙色
结果:页面中渲染出的文字的背景颜色是StudentLqj.vue中.School的颜色:橙色
如果我们项避免出现这种情况解决方法: 1.在不同的子组件中写不同的样式名 2.给每一个子组件中的<style>添加scoped属性 例如:<style scoped> </style>
这样我们写的样式名即使多个组件都一样,也不会出现冲突!
scoped样式属性(局部范围样式)
原理:
原理:当我们用scoped属性来限制样式标签时,在页面渲染时会给用样式的每个<div>中的标签一个随机的id, 根据此id来对应限制的标签,从而达到限制局部效果!
如图:
lang=" "的运用
此属性是供开发者在<style>标签中选择样式语言的(以css和less为例) 1.如果开发者想用css来写样式属性: 举例:
代码语言:javascript复制 <style lang="css" scoped>
.School{
background: rgb(23, 190, 241);
}
</style>
2.如果开发者想用less来写样式属性:
举例:
代码语言:javascript复制 <style lang="less" scoped>
.School{
background: rgb(23, 190, 241);
}
</style>
注意:默认情况下less是vue编译不了的我们必须借助 (终端命令:npm i less-loader)才能正常运行。但也有可能报错:
原因你的vue脚手架调用的webpack的版本兼容问题!
查看webpack版本终端命令中输入:
npm view webpack versions
查看less-loader版本终端命令中输入:
npm view less-loader versions
安装:(我们以7版本为例)
npm i less-loader@7
二、nanoid(类型函数)直接去调用它,就能给出一个唯一的字符串
首先需要安装:
npm i nanoid
它使用分别暴露的形式,所以这样引入:
import {nanoid} from 'nanoid'
举例:
代码语言:javascript复制 <script>
import {nanoid} from 'nanoid'
export default{
name:'Myheader',
methods:{
add(e){//e事件对象
//将用户的输入包装成为一个todos对象
const Obj = {id:nanoid()}
}
}
}
</script>
结果显示:
这个库会随机生成一个id
三、小案例(基础版):
app.vue:
代码语言:javascript复制<template>
<div id="root">
<div class="box">
<span class="haizei">oen piece,记录本</span>
</div>
<div class="todo-container">
<div class="todo-wrap">
<Myheader :addTodos="addTodos"></Myheader>
<List :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"></List>
<Myfooter :todos="todos" :checkAllTodo="checkAllTodo" :clearAllTodo="clearAllTodo"></Myfooter>
</div>
</div>
</div>
</template>
<script>
// 引入School组件
import Myheader from './components/Myheader.vue'
import List from './components/List.vue'
// 因为List是Item的父组件,所以不用引入Item
// import Item from './components/Item.vue'
import Myfooter from './components/Myfooter.vue'
export default {
name: 'App',
// msg里面要接受到的数据开始
components: {Myheader,List,
// Item,
Myfooter,},
data(){
return{
todos:JSON.parse(localStorage.getItem('todos')) || []
// todos:[
// // {id:'001',title:'学vue 写csdn',done:true},
// // {id:'002',title:'吃饭',done:false},
// // {id:'003',title:'学高数 四级',done:false}
// ]
}
},
methods:{
//添加一个todoObj
addTodos(Obj){
this.todos.unshift(Obj)
},
//勾选或取消勾选todoObj
checkTodo(id){
this.todos.forEach((todoObj)=>{
//函数体
if(todoObj.id === id) todoObj.done = !todoObj.done
})
},
//删除一个todoObj
deleteTodo(id){
this.todos = this.todos.filter((todoObj)=>{
return todoObj.id !== id
})
},
//全选or全不选
checkAllTodo(done){
this.todos.forEach((todoObj)=>{
todoObj.done = done
})
},
//清除所有已经完成的todos
clearAllTodo(){
this.todos = this.todos.filter((todoObj)=>{
return !todoObj.done
})
}
},
watch:{
todos:{
deep:true,
handler(value){
localStorage.setItem('todos',JSON.stringify(value))
}
}
}
}
</script>
<style>
/*base*/
body {
background-image: url(../src/assets/1.png) ;
Opacity:1
}
.btn {
display: inline-block;
padding: 4px 12px;
margin-bottom: 0;
font-size: 14px;
line-height: 20px;
text-align: center;
vertical-align: middle;
cursor: pointer;
box-shadow: inset 0 1px 0 rgba(0, 255, 64, 0.2), 0 1px 2px rgba(11, 88, 255, 0.05);
border-radius: 4px;
}
.btn-danger {
color: #fff;
background-color: #1cda45;
border: 1px solid #0d8fe6;
}
.btn-danger:hover {
color: #fff;
background-color: #f70ea9;
}
.btn:focus {
outline: none;
}
.todo-container {
width: 600px;
margin: 0 auto;
}
.todo-container .todo-wrap {
padding: 10px;
border: 1px solid rgb(250, 150, 0);
border-radius: 5px;
}
.box{
display: flex;
flex-direction: row;
justify-content: space-around;
}
.haizei{
font-size: 50px;
color: rgb(245, 147, 0);
font-weight: 800;
}
</style>
List.vue:
代码语言:javascript复制<template>
<ul class="todo-main">
<Item v-for="todoObj in todos"
:key="todoObj.id"
:todoObj="todoObj"
:checkTodo="checkTodo"
:deleteTodo="deleteTodo">
</Item>
</ul>
</template>
<script>
import Item from './Item.vue';
export default{
name:'List',
components:{ Item },
props:['todos','checkTodo','deleteTodo']
}
</script>
<style scoped>
/*main*/
.todo-main {
margin-left: 0px;
border: 1px solid #ddd;
border-radius: 2px;
padding: 0px;
}
.todo-empty {
height: 40px;
line-height: 40px;
border: 1px solid #ddd;
border-radius: 2px;
padding-left: 5px;
margin-top: 10px;
}
</style>
Item.vue:
代码语言:javascript复制<template>
<li>
<label>
<input type="checkbox" :checked="todoObj.done" @change="handleCheck(todoObj.id)"/>
<!-- <input type="checkbox" v-model="todo.done"></input> -->
<span class="ziti">{{todoObj.title}}</span>
<!-- <span>{{todoObj.done}}</span> -->
</label>
<button class="btn btn-danger" @click="handleDelete(todoObj.id)" >删除</button>
</li>
</template>
<script>
export default{
name:'Item',
//生命接受todoObj对象
props:['todoObj','checkTodo','deleteTodo'],
methods:{
//勾选or取消勾选
handleCheck(id){
//通知App组件将对应的todo对象done值取反
this.checkTodo(id)
},
//删除
handleDelete(id){
//通知App组件将对应的todo对象done值取反
if(confirm('确定删除吗?')){
this.deleteTodo(id)
}
},
}
}
</script>
<style scoped>
/*item*/
li {
list-style: none;
height: 36px;
line-height: 36px;
padding: 0 5px;
border-bottom: 1px solid #ddd;
}
li label {
float: left;
cursor: pointer;
}
li label li input {
vertical-align: middle;
margin-right: 6px;
position: relative;
top: -1px;
}
li button {
float: right;
display: none;
margin-top: 3px;
}
li:before {
content: initial;
}
li:last-child {
border-bottom: none;
}
li:hover{
background: rgb(129, 3, 247);
}
li:hover button{
display: block;
}
.ziti{
font-size: 15px;
color: aliceblue;
}
</style>
Myfooter.vue:
代码语言:javascript复制<template>
<div class="todo-footer" v-show="total">
<label>
<input type="checkbox" :checked="isAll" @change="checkAll"/>
</label>
<span class="wenzi">
<span >已完成{{doneTotal}}</span> / 全部{{total}}
</span>
<button class="btn btn-danger" @click="clearAll">清除已完成任务</button>
</div>
</template>
<script>
export default{
name:'Myfooter',
props:['todos','checkAllTodo','clearAllTodo'],
computed:{
total(){
return this.todos.length
},
doneTotal(){
// let i = 0
// this.todos.forEach((todoObj)=>{
// if(todoObj.done) i
// })
// return i
return this.todos.reduce((pre,todoObj)=> pre (todoObj.done ? 1 : 0),0)
},
isAll(){
return this.doneTotal === this.total && this.total > 0
}
},
methods:{
checkAll(e){
this.checkAllTodo(e.target.checked)
},
clearAll(){
this.clearAllTodo()
}
}
}
</script>
<style scoped>
/*footer*/
.todo-footer {
height: 40px;
line-height: 40px;
padding-left: 6px;
margin-top: 5px;
}
.todo-footer label {
display: inline-block;
margin-right: 20px;
cursor: pointer;
}
.todo-footer label input {
position: relative;
top: -1px;
vertical-align: middle;
margin-right: 5px;
}
.todo-footer button {
float: right;
margin-top: 5px;
}
.wenzi{
font-size: 20px;
color: azure;
}
</style>
Myheader.vue:
代码语言:javascript复制<template>
<div class="todo-header">
<input type="text" placeholder="请输入你的任务名称,按回车键确认" v-model="title" @keyup.enter="add"/>
</div>
</template>
<script>
import {nanoid} from 'nanoid'
export default{
name:'Myheader',
props:['addTodos'],
data(){
return{
title:''
}
},
methods:{
add(){
//校验数据
if(!this.title.trim()) return alert('输入不能为空')
//将用户的输入包装成为一个todos对象
const Obj = {id:nanoid(),title:this.title,done:false}
//通知app组件去添加一个todos对象
this.addTodos(Obj)
//清空输入框的内容
this.title=" "
}
}
}
</script>
<style scoped>
/*header*/
.todo-header input {
width: 560px;
height: 28px;
font-size: 14px;
border: 1px solid #ccc;
border-radius: 4px;
padding: 4px 7px;
}
.todo-header input:focus {
outline: none;
border-color: rgba(82, 168, 236, 0.8);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
}
</style>
oen piece是真是存在的!!!
下期见!