ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【Vue】三

2022-05-24 21:00:28  阅读:130  来源: 互联网

标签:Vue vue 组件 import todo id


Vue组件

非单文件组件

一个文件包含多个组件

单文件组件

一个文件只包含一个组件,vue文件初始化:vueInit

<template lang="">
    <div>

    </div>
</template>
<script>
export default {

}
</script>
<style lang="">

</style>

template写组件的结构,script中写组件的交互(数据、方法等),style写组件的样式

<template lang="">
    <div>
        <h2>BookName:{{BookName}}</h2><br/>
        <h2>BookAuthor:{{BookAuthor}}</h2><br/>
        <button @click="alterBookName">BookName</button>       
    </div>
</template>
<script>
    const book = Vue.extend({
        data() {
            return {
                BookName: 'ABC Muder',
                BookAuthor: '阿加莎克里斯蒂'
            }
        },
        methods: {
            alterBookName() {
                alert(this.BookName)
            }
        }
    })
</script>
<style lang="">
    .demo {
        background-color: orange;
    }
</style>

三种组件暴露方式

①分别暴露

    export const book = Vue.extend({
        data() {
            return {
                BookName: 'ABC Muder',
                BookAuthor: '阿加莎克里斯蒂'
            }
        },
        methods: {
            alterBookName() {
                alert(this.BookName)
            }
        }
    })

②统一暴露

    const Book = Vue.extend({
        data() {
            return {
                BookName: 'ABC Muder',
                BookAuthor: '阿加莎克里斯蒂'
            }
        },
        methods: {
            alterBookName() {
                alert(this.BookName)
            }
        }
    })
    export {Book}

③默认暴露

    export default{
        name: 'Book',
        data() {
            return {
                BookName: 'ABC Muder',
                BookAuthor: '阿加莎克里斯蒂'
            }
        },
        methods: {
            alterBookName() {
                alert(this.BookName)
            }
        }
    }

默认暴露使用最多,可以直接将组件配置暴露出来,并且在被import导入的时候不必写{{}}

App.vue 用于管理所有的组件

<template lang="">
    <div>

    </div>
</template>
<script>
    import Book from './Book'
    export default {

        name: 'App',
        components: {
            Book
        }
    }
</script>
<style lang="">

</style>

main.js 用于管理App.vue并创建vm

<template lang="">
    <div>

    </div>
</template>
<script>
    import Book from './Book'
    export default {

        name: 'App',
        components: {
            Book
        }
    }
</script>
<style lang="">

</style>

index.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>Document</title>
</head>
<body>
    <div id="root">

    </div>
    <script src="./js/vue.js"> </script>
    <script src="./main.js"> </script>
</body>
</html>

main.js要在容器声明之后引入,vue.js要在main.js声明之前引入

上面的内容es6语法import浏览器是不能识别的,需要引入脚手架进行翻译

Vue CLI

CLI:command line interface,命令行接口工具

vue create xxx

render模板渲染函数

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')

为什么要设计render函数

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')
[Vue warn]: You are using the runtime-only build of 
Vue where the template compiler is not available. 
Either pre-compile the templates into render functions, 
or use the compiler-included build.

如提示,由于Vue设计,当使用ES6的引入方式时,引入的Vue是不含有模板解析(即解析template)的vue.js,解决方法有两种:

①引入完整版的Vue

import Vue from 'vue/dist/vue'

②使用render函数

new Vue({
  // render: h => h(App),
  render(createElement) {
    return createElement(App)
  }
}).$mount('#app')

改写为箭头函数:

new Vue({
  // render: h => h(App),
  render: h => h(App)
}).$mount('#app')

标签内的template是有专门的vue模板编译器解析,但是main.js必须由render函数解析

ref属性

<template lang="">
    <div>
        <h2 ref="title">BookName:{{BookName}}</h2><br/>
        <h2>BookAuthor:{{BookAuthor}}</h2><br/>
        <button @click="getDomElement" ref="btn">获取DOM元素</button>       
    </div>
</template>
<script>
    export default{
        name: 'Book',
        data() {
            return {
                BookName: 'ABC Muder',
                BookAuthor: '阿加莎克里斯蒂'
            }
        },
        methods: {
            getDomElement() {
                console.log(this.$refs.btn)
            }
        }
    }
</script>
<style lang="">
</style>

1.被用来给元素和子组件注册引用信息 (id的替代者)

2.应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(VueComponent)

3.使用方法

打标识:</xxx ref=xxx>

获取:this.$refs.xxx

props属性:让组件接收外部传来的数据

(1)传递数据

    <Book BookName="ABC谋杀案" BookAuthor="阿加莎克里斯蒂" :BookPrice="30.5"></Book>
    <hr/>
    <Book BookName="Y的悲剧" BookAuthor="埃勒里奎因" :BookPrice="31.5"></Book>

(2)接收数据

①简单接收

props:['BookName', 'BookAuthor', 'BookPrice']

②限制类型和空接收

    export default{
        name: 'Book',
        data() {
            return {
                BookPress: '新星出版社',
                // BookName: this.BookName,
                // BookAuthor: this.BookAuthor,
                // BookPrice: this.BookPrice
            }
        },
        props: {
            BookName: {
                type: String,
                required: true 
            },
            BookAuthor: {
                type: String,
                required: true 
            },
            BookPrice: {
                type: Number,
                required: true 
            }  
        }
    }

如此,外来数据与props类型不一致时就会报错

需求分析:实现对外来数据BookPrice的自增

问题

1. 直接对插值语法中的数据{{BookPrice+1}}的话会出现错误,原因是这里将BookPrice视作了字符串,+1操作会进行字符串拼接

2. 接收数据组件不允许对外来数据进行修改

解决方法

1. 在外来数据传入时使用v-bind,这样“”中的数字就会被当作js表达式,最后被视作number类型而不是字符串

<Book BookName="ABC谋杀案" BookAuthor="阿加莎克里斯蒂" :BookPrice="30.5"></Book>
<hr/>
<Book BookName="Y的悲剧" BookAuthor="埃勒里奎因" :BookPrice="31.5"></Book>

2. 通过组件的this指针,将外来数据的值赋值给data

        data() {
            return {
                BookPress: '新星出版社',
                Name: this.BookName,
                Author: this.BookAuthor,
                Price: this.BookPrice
            }
        },

mixin 混入:把多个组件公用的配置提取成一个混入对象

(1)定义混入mixin.js

export const mixin = {
    methods: {
        AddPrice() {
            this.Price++;
        }
    } 
}

可以配置组件的所有属性(data、方法甚至mounted),除了mounted,冲突时其他优先级均低于组件中的属性

(2)使用混入

全局混入:Vue.mixin(xxx)

局部混入:mixins: [xxx, xxx]

原始方法实现组件间通信

App.vue

<template>
  <div id="app">
    <div id="root">
      <div class="todo-container">
        <div class="todo-wrap">
          <Myheader :addTodo="addTodo"/>
          <list 
            :todos="todos" 
            :checkTodo="checkTodo"
            :deleteOne="deleteOne"
          />
          <Myfooter 
            :selectAll="selectAll" 
            :deleteDone="deleteDone"
            :todos="todos"
          />
        </div>
      </div>
    </div>
  </div>
</template>

<script>
  import Myheader from './components/Myheader.vue'
  import list from './components/list.vue'
  import Myfooter from './components/Myfooter.vue'

  export default {  
    name: 'App',
    components: {
      Myheader,
      list,
      Myfooter
    },
    data() {
      return {
          todos: [
              {id:'001', name:'thingOne', done:true},
              {id:'002', name:'thingTwo', done:false},
              {id:'003', name:'thingThere', done:true}
          ]
      }
    },
    methods: {
      addTodo(todo) {
        // console.log(todo)
        this.todos.unshift(todo)
      },
      selectAll(x) {
        // console.log(x)
        for(var index in this.todos) {
          this.todos[index].done = x
        }
      },
      checkTodo(id) {
        // console.log(id)
        this.todos.forEach(todo => {
          if(todo.id === id) {
            todo.done = !todo.done
          }
        });
      },
      deleteDone() {
        this.todos = this.todos.filter(function(e) {
          return !e.done
        })
      },
      deleteOne(id) {
        this.todos = this.todos.filter(function(e) {
          return e.id !== id
        })
      } 
    }
  }
</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;
  }
  /*base*/
  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>

Myheader.vue

<template lang="">
    <div>
        <div class="todo-header">
            <input type="text" placeholder="请输入你的任务名称,按回车键确认" @keyup.enter="add"/>
        </div>
    </div>
</template>
<script>
    import {nanoid} from 'nanoid'
    export default {
        name: 'Myheader',
        methods: {
            add(e) {
                let todoObj = {
                    id: nanoid(),
                    name: e.target.value,
                    done: false
                }
                this.addTodo(todoObj)
            }
        },
        props: ['addTodo']
    }
</script>
<style>
    /*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>

list.vue

<template lang="">
    <div>
        <ul class="todo-main">
            <Myitem 
                v-for="todoObj in todos" 
                :key="todoObj.id" 
                :todo="todoObj" 
                :checkTodo='checkTodo'
                :deleteOne="deleteOne"
            />
        </ul>
    </div>
</template>
<script>
    import Myitem from './Myitem.vue'

    export default {
        name: 'list',
        components: {
            Myitem
        },
        props: ['todos', 'checkTodo', 'deleteOne']
    }
</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 lang="">
    <li>
        <label>
            <input type="checkbox" :checked="todo.done" @click="handleCheck(todo.id)"/>
            <!-- 以下代码也能实现功能但是修改了props元素,不推荐使用 -->
            <!-- <input type="checkbox" v-model="todo.done" /> -->
            <span>{{todo.name}}</span>
        </label>
        <button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button>
    </li>
</template>
<script>
    export default {
        name: 'Myitem',
        props:['todo', 'checkTodo', 'deleteOne'],
        methods: {
            handleCheck(id) {
                // console.log(id)
                this.checkTodo(id)
            },
            handleDelete(id) {
                if(confirm("确定删除吗")) {
                    this.deleteOne(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 lang="">
    <div>
        <div class="todo-footer" v-show="total">
            <label>
              <!-- <input type="checkbox" :checked="isAll" @click="handelSelectAll" /> -->
              <input type="checkbox" v-model="isAll" />
            </label>
            <span>
              <span>已完成 {{numberOfDone}} </span>/全部 {{total}}
            </span>
            <button class="btn btn-danger" @click="deleteTodos">清除已完成任务</button>
        </div>
    </div>
</template>
<script>
  export default {
      name: 'Myfooter',
      methods: {
        deleteTodos() {
          this.deleteDone()
        },
        handelSelectAll(e) {
          this.selectAll(e.target.checked)
        }
      },
      computed: {
        total() {
          return this.todos.length
        },
        numberOfDone() {
          // var count = 0
          // this.todos.forEach(todo => {
          //   if(todo.done) {
          //     count++
          //   }
          // });
          // return count
            const x = this.todos.reduce((pre, current)=>{
              return pre + (current.done ? 1 : 0)
            },0)

            return x
        },
        isAll: {
          get() {
            return this.total === this.numberOfDone && this.numberOfDone > 0
          },
          set(val) {
            this.selectAll(val)
          }
        }
      },
      props:['selectAll', 'deleteDone', 'todos']
  }
</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和Item跨组件通信

App.vue

<template>
  <div id="app">
    <div id="root">
      <div class="todo-container">
        <div class="todo-wrap">
          <Myheader @addTodo="addTodo"/>
          <list 
            :todos="todos"
            ref="list" 
          />
          <Myfooter 
            @selectAll="selectAll" 
            @deleteDone="deleteDone"
            :todos="todos"
          />
        </div>
      </div>
    </div>
  </div>
</template>

<script>
  import Myheader from './components/Myheader.vue'
  import list from './components/list.vue'
  import Myfooter from './components/Myfooter.vue'

  export default {  
    name: 'App',
    components: {
      Myheader,
      list,
      Myfooter
    },
    data() {
      return {
          todos: JSON.parse(localStorage.getItem('todos')) || []
      }
    },
    watch: {
      todos: {
        handler(val) {
          localStorage.setItem('todos', JSON.stringify(val))
        },
        deep: true
      }
    },
    methods: {
      addTodo(todo) {
        // console.log(todo)
        this.todos.unshift(todo)
      },
      selectAll(x) {
        // console.log(x)
        for(var index in this.todos) {
          this.todos[index].done = x
        }
      },
      checkTodo(id) {
        // console.log(id)
        this.todos.forEach(todo => {
          if(todo.id === id) {
            todo.done = !todo.done
          }
        });
      },
      deleteDone() {
        this.todos = this.todos.filter(function(e) {
          return !e.done
        })
      },
      deleteOne(id) {
        this.todos = this.todos.filter(function(e) {
          return e.id !== id
        })
      } 
    },
    // @checkTodo='checkTodo'
    // @deleteOne="deleteOne"
    mounted() {
      let items = this.$refs.list.$refs.item
      items.forEach(item => {
        item.$on('checkTodo', this.checkTodo)
        item.$on('deleteOne', this.deleteOne)
      });
    }
  }
</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;
  }
  /*base*/
  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>

由于App和MyItem中间隔着一个Mylist,所以能够实现两个组件的通信重点是以下代码

          <list 
            :todos="todos"
            ref="list" 
          />
    mounted() {
      let items = this.$refs.list.$refs.item
      items.forEach(item => {
        item.$on('checkTodo', this.checkTodo)
        item.$on('deleteOne', this.deleteOne)
      });
    }

MyItem.vue

<template lang="">
    <li>
        <label>
            <input type="checkbox" :checked="todo.done" @click="handleCheck(todo.id)"/>
            <!-- 以下代码也能实现功能但是修改了props元素,不推荐使用 -->
            <!-- <input type="checkbox" v-model="todo.done" /> -->
            <span>{{todo.name}}</span>
        </label>
        <button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button>
    </li>
</template>
<script>
    export default {
        name: 'Myitem',
        props:['todo'],
        methods: {
            handleCheck(id) {
                // console.log(id)
                this.$emit('checkTodo', id)
            },
            handleDelete(id) {
                if(confirm("确定删除吗")) {
                    this.$emit('deleteOne', 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>

使用全局事件总线实现App与MyItem的通信

App.vue

    mounted() {
      this.bus.$on('checkTodo', this.checkTodo)
      this.bus.$on('deleteOne', this.deleteOne)
    }

MyItem.vue

    export default {
        name: 'Myitem',
        props:['todo'],
        methods: {
            handleCheck(id) {
                this.bus.$emit('checkTodo', id)
            },
            handleDelete(id) {
                if(confirm("确定删除吗")) {
                    this.bus.$emit('deleteOne', id)
                }
            }
        }
    }

Vue插件

(1)创建插件配置文件 plugins.js

export default {
    install(Vue) {
        Vue.filter('mySlice', function(val) {
            return val.slice(0, 3)
        })
    }
}

可以定义过滤器、自定义指令以及混入

(2)在main.js中引入并使用插件

import plugins from './plugins'
Vue.use(plugins)

(3)在组件中使用插件中的过滤器

<h3>BookAuthor:{{Author | mySlice}}</h3><br/>

scoped样式

使得css样式只能在此组件中使用

路由简介

路由的基本使用

1 安装路由

npm i vue-router

2 在Vue入口文件main.js中导入并使用路由插件

import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router'
import router from './router'

Vue.config.productionTip = false
Vue.use(VueRouter)

new Vue({
  render: h => h(App),
  router: router
}).$mount('#app')

导入VueRouter的目的是为了在new Vue中使用router属性

导入的router为自定义的路由index.js配置文件

3 配置路由index.js文件

import VueRouter from 'vue-router' 
import Hello from '../components/Hello'
import Welcome from '../components/Welcome'

export default new VueRouter ({
    routes: [
        {
            path: '/Hello',
            component: Hello
        },
        {
            path: '/Welcome',
            component: Welcome
        }
    ]
})

注意这里是routes(多个路由)而不是routers(多个路由器)

4 在组件中使用路由

<div class="row">
      <div class="col-xs-2 col-xs-offset-2">
        <div class="list-group">
          <!-- <a class="list-group-item active" href="./about.html">About</a>
          <a class="list-group-item" href="./home.html">Home</a> -->
          <router-link class="list-group-item" active-class="active" to="/Welcome">Welcome</router-link>
          <router-link class="list-group-item" active-class="active" to="/Hello">Hello</router-link>
        </div>
      </div>
      <div class="col-xs-6">
        <div class="panel">
          <div class="panel-body">
            <router-view></router-view>
          </div>
        </div>
      </div>
    </div>

router-link 可被渲染为a标签,用于实现路由的切换

router-view用于指定路由指向的组件的呈现位置

路由组件和一般组件

1 路由组件一般放在pages,一般组件放在components

2 通过切换,‘隐藏’了的路由组件,默认是被销毁了的,需要的时候再去挂载

3 每个组件都有着自己的$route属性,存储着自己的路由信息

4 整个应用只有一个路由器,可以用组件的$router属性获取

嵌套路由

移动端UI组件库

1 Vant

2 Cube UI

3 Mint UI

PC端UI组件库

1 ElementUI

2 IViewUI

安装
npm i element-ui -S
在main.js中引用注册elementUI
import Vue from 'vue'
import App from './App.vue'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'

Vue.config.productionTip = false
Vue.use(ElementUI)

new Vue({
  render: h => h(App),
}).$mount('#app')
按需引入

借助 babel-plugin-component,我们可以只引入需要的组件,以达到减小项目体积的目的。

首先,安装 babel-plugin-component:

npm install babel-plugin-component -D

然后,将babel.config.js 修改为:

module.exports = {
  presets: [
    '@vue/cli-plugin-babel/preset',
    ["@babel/preset-env", { "modules": false }],
  ],
  plugins: [
    [
      "component",
      {
        "libraryName": "element-ui",
        "styleLibraryName": "theme-chalk"
      }
    ]
  ]
}

标签:Vue,vue,组件,import,todo,id
来源: https://www.cnblogs.com/tod4/p/16307149.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有