Vue学习(四)

Vue 手脚架

脚手架文件结构

├── node_modules 
├── public
│   ├── favicon.ico: 页签图标
│   └── index.html: 主页面
├── src
│   ├── assets: 存放静态资源
│   │   └── logo.png
│   │── component: 存放组件
│   │   └── HelloWorld.vue
│   │── App.vue: 汇总所有组件
│   │── main.js: 入口文件
├── .gitignore: git版本管制忽略的配置
├── babel.config.js: babel的配置文件
├── package.json: 应用包配置文件 
├── README.md: 应用描述文件
├── package-lock.json:包版本控制文件

关于不同版本的Vue

  1. vue.jsvue.runtime.xxx.js的区别:
    vue.js是完整版的Vue,包含:核心功能 + 模板解析器
    vue.runtime.xxx.js是运行版的Vue,只包含:核心功能;没有模板解析器。
  2. 因为vue.runtime.xxx.js没有模板解析器,所以不能使用template这个配置项,需要使用render函数接收到的createElement函数去指定具体内容。

vue.config.js配置文件

  1. 使用vue inspect > output.js可以查看到Vue脚手架的默认配置。
  2. 使用vue.config.js可以对脚手架进行个性化定制

ref属性

  1. 被用来给元素或子组件注册引用信息(id的替代者)
  2. 应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)
  3. 使用方式:
    1. 打标识:<h1 ref="xxx">.....</h1><School ref="xxx"></School>
    2. 获取:this.$refs.xxx
<template>
  <div>
      <h1 v-text="msg" ref="title"></h1>
      <button ref="btn" @click="showDOM">点我输出上方的DOM元素</button>
      <School ref="sch"></School>
  </div>
</template>

<script>
//引入School组件
import School from './components/School.vue'
    export default {
        name:'App',
        data() {
            return {
                msg:'欢迎学习Vue!'
            }
        },
        components:{School},
        methods: {
            showDOM(){
                console.log(this.$refs.title) //真实DOM元素
                console.log(this.$refs.btn)  //真实DOM元素
                console.log(this.$refs.sch)  //School组件的实例对象
            }
        }
    }
</script>


props配置项

  1. 功能:让组件接收外部传过来的数据

  2. 传递数据:<Demo name="xxx"/>

  3. 接收数据:

    ①第一种方式(只接收):props:['name']

    ②第二种方式(限制类型):props:{name:String}

    ③第三种方式(限制类型、限制必要性、指定默认值):

Student.vue文件

<template>
  <div>
      <h1 v-text="msg"></h1>
      <h2>学生姓名:{{name}}</h2>
      <h2>学生性别:{{sex}}</h2>
      <h2>学生年龄:{{myAge}}</h2>
      <button @click="updateAge">尝试修改收到的年龄</button>
  </div>
</template>

<script>
    export default {
        name:'Student',
        data() {
            return {
                msg:'我是一个尚硅谷的学生',
                myAge:this.age
            }
        },
        methods: {
			updateAge(){
				this.myAge++
			}
		},
        // props:['name','age','sex']  简单声明接收
        /*接收的同时对数据进行类型限制
        props:{
            name:String,
            age:Number,
            sex:String
        }
        */
       //接收的同时对数据进行类型限制+默认值的指定+必要性的限制
       props:{
           name:{
               type:String, //name的类型是字符串
               required:true //name是必要的
           },
           age:{
               type:Number,
               default:99 //默认值
           },
           sex:{
               type:String,
               required:true
           }
       }
    }
</script>

App.vue文件

<template>
  <div>
      <Student name="李四" sex="女" :age="18"/>
  </div>
</template>

<script>

import Student from './components/Student.vue'
    export default {
        name:'App',
        components:{Student}
    }
</script>


注意props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据。

mixin(混入)

  1. 功能:可以把多个组件共用的配置提取成一个混入对象

  2. 使用方式:

    第一步定义混合:

    {
        data(){....},
        methods:{....}
        ....
    }
    

    第二步使用混入:

    ​ 全局混入:Vue.mixin(xxx)
    ​ 局部混入:mixins:['xxx']

插件

  1. 功能:用于增强Vue

  2. 本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据。

  3. 定义插件:

    对象.install = function (Vue, options) {
        // 1. 添加全局过滤器
        Vue.filter(....)
    
        // 2. 添加全局指令
        Vue.directive(....)
    
        // 3. 配置全局混入(合)
        Vue.mixin(....)
    
        // 4. 添加实例方法
        Vue.prototype.$myMethod = function () {...}
        Vue.prototype.$myProperty = xxxx
    }
    
  4. 使用插件:Vue.use()

示例:
main.js文件

//引入Vue
import Vue from 'vue'
//引入app
import App from './App.vue'
//引入插件
import plugins from './plugins'
//关闭Vue的生产提示
Vue.config.productionTip = false
//应用插件
Vue.use(plugins)
//创建vm
new Vue({
    el:'#app',
    render: h => h(App)
})

plugins.js文件

export default {
    install(){
        console.log('@@@install')
    }
}

scoped样式

  1. 作用:让样式在局部生效,防止冲突。
  2. 写法:<style scoped></style>

TodoList案例

在这里插入图片描述
MyHeader.vue文件

<template>
    <div class="todo-header">
        <input type="text" placeholder="请输入你的任务名称,按回车键确认" v-model="title" @keyup.enter="add"/>
    </div>
</template>

<script>
import {nanoid} from 'nanoid' //引入生成唯一id的库
export default {
    name:'MyHeader',
    props:['addTodo'],
    data() {
        return {
            title:''
        }
    },
    methods: {
        add(){
            //校验数据
            if(!this.title) return alert('输入不能为空')
            //将用户的输入包装成一个todo对象
            const todoObj = {id:nanoid(),title:this.title,done:false}
            //通知App组件去添加一个todo对象
            this.addTodo(todoObj)
            //清空输入
            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>

MyList.vue文件

<template>
    <ul class="todo-main">
        <MyItem 
            v-for="todoObj in todos"
             :key="todoObj.id" 
             :todo="todoObj" 
             :checkTodo="checkTodo"
             :deleteTodo="deleteTodo"
        />
    </ul>
</template>

<script>
import MyItem from './MyItem.vue'
export default {
    name:'MyList',
    components:{MyItem},
    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>

MyItem.vue文件

<template>
    <li>
        <label>
            <input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)"/>
            <!-- 如下代码也能实现功能,但是并不太推荐,因为有点违反原则,修改了props -->
            <!-- <input type="checkbox" v-model="todo.done"/> -->
            <span>{{todo.title}}</span>
        </label>
        <button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button>
    </li>
</template>

<script>
export default {
    name:'MyItem',
    //声明接收todo对象
    props:['todo','checkTodo','deleteTodo'],
    methods: {
        //勾选或取消勾选
        handleCheck(id){
            //让App组件将对应的todo对象的done值取反
            this.checkTodo(id)
        },
        //删除
        handleDelete(id){
            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-color: #ddd;
    }

    li:hover button{
        display: block;
    }
</style>

MyFooter.vue文件

<template>
        <div class="todo-footer" v-show="total">
        <label>
          <!-- <input type="checkbox" :checked="isAll" @change="checkAll"/> -->
          <input type="checkbox" v-model="isAll"/>
        </label>
        <span>
          <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(){
        // const x = this.todos.reduce((pre,current)=>{
        //   console.log('@',pre,current)
        //   return pre+(current.done ? 1 : 0)
        // },0)
        return this.todos.reduce((pre,todo)=>pre + (todo.done ? 1 : 0),0)
      },
      isAll:{
        get(){
           return this.doneTotal === this.total && this.total > 0
        },
        set(value){
          this.checkAllTodo(value)
        }
      }

    },
    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;
    }
</style>

App.vue文件

<template>
  <div id="root">
  <div class="todo-container">
    <div class="todo-wrap">
      <MyHeader :addTodo="addTodo"/>
      <MyList :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"/>
      <MyFooter :todos="todos" :checkAllTodo="checkAllTodo" :clearAllTodo="clearAllTodo"/>
    </div>
  </div>
</div>

</template>

<script>

import MyHeader from './components/MyHeader.vue'
import MyList from './components/MyList.vue'
import MyFooter from './components/MyFooter.vue'
    export default {
        name:'App',
        components:{MyHeader,MyList,MyFooter},
        data() {
            return {
                todos:[
                    {id:'001',title:'吃饭',done:true},
                    {id:'002',title:'睡觉',done:true},
                    {id:'003',title:'打游戏',done:false}
                ]
            }
        },
        methods: {
          //添加一个todo
          addTodo(todoObj){
            this.todos.unshift(todoObj)
          },
          //勾选或取消勾选一个todo
          checkTodo(id){
            this.todos.forEach((todo)=>{
              if(todo.id == id) todo.done = !todo.done
            })
          },
          //删除一个todo
          deleteTodo(id){
            this.todos = this.todos.filter((todo)=>{
              return todo.id !== id
            })
          },
          //全选或者取消全选
          checkAllTodo(done){
            this.todos.forEach((todo)=>{
              todo.done = done
            })
          },
          //清除所有已经完成的todo
          clearAllTodo(){
            this.todos = this.todos.filter((todo)=>{
              return !todo.done
            })
          }
        },
    }
</script>

<style>
    body {
    background: #fff;
    }

    .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(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
    border-radius: 4px;
    }

    .btn-danger {
    color: #fff;
    background-color: #da4f49;
    border: 1px solid #bd362f;
    }
    .btn-danger:hover {
    color: #fff;
    background-color: #bd362f;
    }
    .btn:focus {
    outline: none;
    }
    .todo-container {
    width: 600px;
    margin: 0 auto;
    }
    .todo-container .todo-wrap {
    padding: 10px;
    border: 1px solid #ddd;
    border-radius: 5px;
    }
</style>

效果:
在这里插入图片描述

总结TodoList案例

  1. 组件化编码流程:

    ​ (1)拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突。

    ​ (2)实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:

    ​ ①一个组件在用:放在组件自身即可。

    ​ ② 一些组件在用:放在他们共同的父组件上(状态提升)。

    ​ (3)实现交互:从绑定事件开始。

  2. props适用于:

    ​ (1).父组件 →子组件 通信

    ​ (2).子组件 → 父组件 通信(要求父先给子一个函数)

  3. 使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改的!

  4. props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这样做。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值