предисловие
В этой статье TodoList реализуется с использованием пяти базовых знаний Vue: базовых знаний, знаний о компонентах, инженерных знаний, маршрутизации (vue-router) и управления состоянием (vuex). Хотя в Vue используются разные точки знаний, конечный эффект в основном одинаков, поэтому это также пример внешнего вида.
- Основная примерная главаПродемонстрировать использование основных команд в vue;
- Глава о компонентизацииДемонстрировать разбиение компонентов и передачу значений между компонентами;
- Инженерная главаПродемонстрировать, как использовать webpack для разработки проекта;
- Глава о маршрутизации vue-routerПродемонстрировать, как использовать маршрутизацию для перехода между страницами;
- глава управления состоянием vuexПродемонстрируйте, как управлять состоянием с помощью vuex;
Конечно, эта статья содержит только малую часть знаний о Vue, основная причина в том, что я тоже новичок во фронтенде, и на прошлой неделе я неделю изучал и продавал Vue. Эта статья также является кратким изложением уроков прошлой недели! В то же время я надеюсь, что это может быть полезно для студентов, которые изучают или будут изучать Vue.
Путь в тысячу миль начинается с подчиненных! Начнем драться! 💪💪💪
визуализация
Основные функции
- Добавить к
待办事项
; -
待办事项
отображение списка; - фильтровать по статусу
待办事项
список; - Статистика по штатам
待办事项
количество; - Исправлять
待办事项
условие;
базовый экземпляр
ключевая технология
- использовать
v-bind
Директивы и синтаксический сахар:
; - использовать
v-model
Директивы демонстрируют привязку данных; - использовать
v-on
Инструкции реализуют взаимодействие и синтаксический сахар@
; - использовать
v-if
,v-else
Директивы отображают различные элементы; - использовать
v-for
инструкция перебирает массив; - Использование связанных свойств в конструкторе vue;
полный код
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TodoList</title>
<style>
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
#app {
width: 800px;
height: 600px;
margin: 0 auto;
background-color: rgb(245, 245, 245);
padding: 24px 0;
}
#app .header {
font-size: 48px;
text-align: center;
}
p,
div {
font-size: 16px;
margin-left: 15px;
padding: 10px;
}
ul,
li {
list-style: none;
}
label {
padding: 5px;
font-size: 13px;
}
.txt_input {
width: 90%;
height: 30px;
padding: 10px 10px;
font-size: 14px;
border: 1px solid transparent;
}
.add_button {
width: 60px;
height: 30px;
}
li span {
display: inline-block;
width: 90%;
}
li button {
font-size: 10px;
width: 45px;
height: 25px;
}
</style>
</head>
<body>
<div id="app">
<header class="header">My Todo List</header>
<p>当前共有{{todos.length}}个代办事项,已完成{{completed}}个,剩余{{todos.length-completed}}个。</p>
<p>
<!--@click='filterList(0) 是 v-on:click='filterList(0)的缩写-->
<input type="radio" v-model="picked" value="0" @click='filterList(0)'><label>所有</label>
<input type="radio" v-model="picked" value="1" @click='filterList(1)'><label>已完成</label>
<input type="radio" v-model="picked" value="2" @click='filterList(2)'><label>未完成</label>
</p>
<div class="div">
<input type="text" class="txt_input" placeholder="请输入代办事项名称" v-model.trim="todo">
<!--:disabled="isDisabled" 是v-bind:disabled="isDisabled"的缩写-->
<button :disabled="isDisabled" @click="addTodo" class="add_button">添加</button>
</div>
<ul>
<li v-for="(todo,index) in tempTodos" v-bind:style="setStyle(todo)" :key="index">
<div>
<span>{{index+1}}. {{todo.name}}</span>
<button v-if="todo.status" @click="changeStatus(index)">已完成</button>
<button v-else="todo.status" @click="changeStatus(index)">未完成</button>
</div>
</li>
</ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var app = new Vue({
el: '#app', //vue对象挂载带id为app的元素上
data: {
todo: '', //待办事项名称
picked: 0,
isDisabled: false, //是否可以点击添加按钮
tempTodos: [],
// 初始化数据
todos: [
{ name: 'JavaScript', status: true },
{ name: 'Css', status: false },
{ name: 'Vue', status: true }
]
},
//计算属性
computed: {
//当属性改变时自动计算已完成的待办事项数量
completed: function () {
return this.todos.filter(x => x.status).length;
}
},
methods: {
//添加代办事项
addTodo: function () {
this.todos.push({ name: this.todo, status: false });
this.todo = '';
},
//当待办事项未完成时,字体颜色显示为红色
setStyle: function (obj) {
if (!obj.status)
return "color:red";
},
//修改待办事项状态
changeStatus: function (index) {
this.tempTodos[index].status = !this.tempTodos[index].status;
//更新数据
this.filterList(parseInt(this.picked));
},
//根据条件过滤待办事项
filterList: function (type) {
this.isDisabled = !!type;
switch (type) {
case 0:
this.tempTodos = this.todos;
break;
case 1:
this.tempTodos = this.todos.filter(x => x.status);
break;
case 2:
this.tempTodos = this.todos.filter(x => !x.status);
break;
}
}
},
//vue生命周期函数,在组件挂载完成后调用
mounted() {
this.tempTodos = this.todos;
}
});
</script>
</body>
</html>
составной
В этот раздел будет добавлено
待办事项
функция и待办事项
Список отображения — это два разных компонента.
ключевая технология
- как использовать компоненты;
- Родительские компоненты передают значения дочерним компонентам;
- Дочерние компоненты передают значения родительским компонентам;
- Компоненты, не имеющие отношения друг к другу, передают значения;
- Как зарегистрировать локальные компоненты;
- Изменения атрибута данных компонента;
полный код
HTML-страницы и стили можно копировать напрямуюбазовый экземплярКод в главе, здесь дана только измененная часть.
<body>
<div id="app">
<header class="header">My Todo List</header>
<!--添加待办事项组件-->
<add-todo @add="add"></add-todo>
<!--待办事项列表展示组件,向子组件传递属性todos-->
<todo-list :todos="todos"></todo-list>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
//添加todo组件
const AddTodo = {
template: `
<div class="div">
<input type="text" class="txt_input" placeholder="请输入代办事项名称" v-model.trim="todo">
<button :disabled="canAddTodo" @click="add" class="add_button">添加</button>
</div>
`,
data() {
return {
todo: '',
canAddTodo: false
}
},
methods: {
add() {
//触发父组件中的add属性绑定的函数
this.$emit('add', this.todo);
this.todo = '';
}
},
//vue声明周期,在组件初始化之前创建selectChanged监听事件
created() {
this.$root.$on('selectChanged', value => {
this.canAddTodo = !!value;
});
}
};
//todo列表展示组件
const TodoList = {
template: `
<div>
<p>
<input type="radio" v-model="picked" value="0" @click='filterTodos(0)'><label>所有</label>
<input type="radio" v-model="picked" value="1" @click='filterTodos(1)'><label>已完成</label>
<input type="radio" v-model="picked" value="2" @click='filterTodos(2)'><label>未完成</label>
</p>
<p>当前共有{{todoList.length}}个代办事项,已完成{{completed}}个,剩余{{todoList.length-completed}}个。</p>
<ul>
<li v-for="(todo,index) in tempTodoList" :key="index">
<div>
<span>{{index+1}}. {{todo.name}}</span>
<button v-if="todo.status" @click="changeStatus(index)">已完成</button>
<button v-else="todo.status" @click="changeStatus(index)">未完成</button>
</div>
</li>
</ul>
</div>
`,
props: ['todos'], //接受父组件传递的对象
// 组件中的data是一个函数
data: function () {
return {
todoList: this.todos,
picked: 0,
tempTodoList:[]
}
},
computed: {
completed() {
return this.todoList.filter(x => x.status).length;
}
},
methods: {
changeStatus(index) {
this.tempTodoList[index].status = !this.tempTodoList[index].status;
this.filterTodos(parseInt(this.picked));
},
filterTodos(type) {
//无关系组件之间的通信(触发AddTodo组件中的selectChanged事件)
this.$root.$emit('selectChanged', type);
switch (type) {
case 0:
this.tempTodoList = this.todoList;
break;
case 1:
this.tempTodoList = this.todoList.filter(x => x.status);
break;
case 2:
this.tempTodoList = this.todoList.filter(x => !x.status);
break;
}
}
},
mounted(){
this.tempTodoList = this.todoList;
}
};
var app = new Vue({
el: '#app',
// 注册局部组件
components: {
'todo-list': TodoList,
'add-todo': AddTodo
},
data: {
todos: [
{ name: 'JavaScript', status: true },
{ name: 'Css', status: false },
{ name: 'Vue', status: true }
],
bus: new Vue(),//创建新的Vue实例,用于无关系组件之间的通信
},
methods: {
add(todo) {
this.todos.push({ name: todo, status: false });
}
}
});
</script>
</body>
Инжиниринг
Структура каталогов
ключевое знание
- Как построить проект фундамента;
- базовая конфигурация вебпака;
-
.vue
содержимое файла компонента;
полный код
шаг 1
//初始化
npm init -y
npm i webpack webpack-cli -g
//注意babel与babel-loader的版本
npm i babel babel-loader@7 babel-core babel-plugin-transform-runtime babel-preset-es2015 babel-runtime -D
npm i webpack-dev-server css-loader style-loader -D
npm i vue vue-loader vue-style-loader vue-template-compiler vue-hot-reload-api -S
Шаг 2
конфигурационный файл вебпака
const path = require('path');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const config = {
entry: {
main: './app.js'
},
output: {
path: path.resolve(__dirname, 'dist'),
publicPath: '/dist/',
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.vue$/,
use: 'vue-loader',
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.js$/,
use: 'babel-loader',
exclude: /node_modules/
}
]
},
plugins: [
new VueLoaderPlugin()
]
}
module.exports = config;
Шаг 3
Отредактируйте файл index.html.
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
</div>
<script type="text/javascript" src="/dist/bundle.js"></script>
</body>
</html>
Шаг 4
Файл конфигурации .bablerc
{
"presets": [
"es2015"
],
"plugins": [
"transform-runtime"
]
}
Шаг 5
Входной файл app.js
import Vue from 'vue';
import app from './app/App.vue';
new Vue({
el: '#app',
render: h => h(app)
})
Шаг 6
./app/App.vue
<template>
<div class="content">
<header class="header">My Todo List</header>
<add-todo @add="add"></add-todo>
<todo-list :todos="todos"></todo-list>
</div>
</template>
<script>
import Vue from "vue";
import AddTodo from "./components/AddTodo.vue";
import TodoList from "./components/TodoList.vue";
export default {
components: {
AddTodo,
TodoList
},
data() {
return {
todos: [
{ name: "JavaScript", status: true },
{ name: "Css", status: false },
{ name: "Vue", status: true }
],
bus: new Vue()
};
},
methods: {
add(todo) {
this.todos.push({ name: todo, status: false });
}
}
};
</script>
<style scoped>
.content {
width: 800px;
height: 600px;
margin: 0 auto;
background-color: rgb(245, 245, 245);
padding: 24px 0;
}
.header {
font-size: 48px;
text-align: center;
}
</style>
./app/components/TodoList.vue
<template>
<div class="content-div">
<p class="content-div">
<input type="radio" v-model="picked" value="0" @click="filterTodos(0)" />
<label>所有</label>
<input type="radio" v-model="picked" value="1" @click="filterTodos(1)" />
<label>已完成</label>
<input type="radio" v-model="picked" value="2" @click="filterTodos(2)" />
<label>未完成</label>
</p>
<p
class="content-div"
>当前共有{{todoList.length}}个代办事项,已完成{{completed}}个,剩余{{todoList.length-completed}}个。</p>
<ul class="list">
<li class="item" v-for="(todo,index) in tempTodoList" :key="index">
<div>
<span class="item">{{index+1}}. {{todo.name}}</span>
<button v-if="todo.status" @click="changeStatus(index)">已完成</button>
<button v-else @click="changeStatus(index)">未完成</button>
</div>
</li>
</ul>
</div>
</template>
<script>
export default {
props: ["todos"],
data: function() {
return {
todoList: this.todos,
picked: 0,
tempTodoList: []
};
},
computed: {
completed() {
return this.todoList.filter(x => x.status).length;
}
},
methods: {
changeStatus(index) {
this.tempTodoList[index].status = !this.tempTodoList[index].status;
this.filterTodos(parseInt(this.picked));
},
filterTodos(type) {
this.$root.$emit("selectChanged", type);
switch (type) {
case 0:
this.tempTodoList = this.todoList;
break;
case 1:
this.tempTodoList = this.todoList.filter(x => x.status);
break;
case 2:
this.tempTodoList = this.todoList.filter(x => !x.status);
break;
}
}
},
mounted() {
this.tempTodoList = this.todoList;
}
};
</script>
<style scoped>
.content-div {
font-size: 16px;
margin-left: 15px;
}
.list {
list-style: none;
padding: 0px;
margin-left: 15px;
}
.item {
list-style: none;
display: inline-block;
width: 90%;
padding-top: 10px;
}
</style>
./app/components/AddTodo.vue
<template>
<div class="add-content">
<input type="text" class="text_input" placeholder="请输入代办事项名称" v-model.trim="todo" />
<button :disabled="canAddTodo" @click="add" class="add_button">添加</button>
</div>
</template>
<script>
export default {
created() {
this.$root.$on("selectChanged", value => {
this.canAddTodo = !!value;
});
},
data() {
return {
todo: "",
canAddTodo: false
};
},
methods: {
add() {
this.$emit("add", this.todo);
this.todo = "";
}
}
};
</script>
<style scoped>
.add-content {
font-size: 16px;
margin-left: 15px;
padding: 10px;
}
.text_input {
width: 90%;
height: 30px;
padding: 10px 10px;
font-size: 14px;
border: 1px solid transparent;
}
.add_button {
width: 60px;
height: 30px;
}
</style>
vue-router
Эта глава будет в предыдущем разделеИнжинирингНа основе измените TodoList.
Структура каталогов
Скриншот страницы
ключевое знание
- Базовое использование vue-router;
- использовать кеш браузера для сохранения данных;
- Каковы недостатки такого мышления?
полный код
шаг 1
npm i vue-router -S
Шаг 2
Изменить webpack.config.js
const path = require('path');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const config = {
...
module: {
rules: [
{
test: /\.vue$/,
use: 'vue-loader',
}
...
]
},
plugins: [
new VueLoaderPlugin()
]
}
Шаг 3
app.js
import Vue from 'vue';
import VueRouter from 'vue-router';
import App from './App.vue'
import Index from './views/All.vue';
import Completed from './views/Completed.vue';
import Uncompleted from './views/UnCompleted.vue';
Vue.use(VueRouter);
const routerPush = VueRouter.prototype.push;
VueRouter.prototype.push = function push(location) {
return routerPush.call(this, location).catch(error => error)
}
const routes = [
{
path: '/',
redirect: '/index'
}, {
path: '/index',
name: 'all',
component: Index
}, {
path: '/completed',
name: 'completed',
component: Completed
}, {
path: '/uncompleted',
name: 'uncompleted',
component: Uncompleted
}
];
const router = new VueRouter({
routes
})
new Vue({
el: '#app',
router,
render: h => h(App)
})
Шаг 4
App.vue
<template>
<div class="content">
<header class="header">My Todo List</header>
<div class="add-content">
<input type="text" class="text_input" placeholder="请输入代办事项名称" v-model.trim="todo" />
<button @click="add" class="add_button">添加</button>
<p
class="content-div"
>当前共有{{todoList.length}}个代办事项,已完成{{completed}}个,剩余{{todoList.length-completed}}个。</p>
<p class="content-div">
<router-link to="/index">全部</router-link>
<router-link to="/completed">已完成</router-link>
<router-link to="/uncompleted">未完成</router-link>
</p>
</div>
<router-view ></router-view>
</div>
</template>
<script>
export default {
data() {
return {
todo: "",
todoList: [],
picked: 0,
};
},
computed: {
completed() {
this.todoList = JSON.parse(window.localStorage.getItem("todos"));
return this.todoList.filter(x => x.status).length;
}
},
methods: {
add() {
this.picked = 0;
this.todoList.push({ name: this.todo, status: false });
window.localStorage.setItem("todos", JSON.stringify(this.todoList));
this.$router.go(0);
}
},
mounted() {
this.todoList = [
{ name: "JavaScript", status: true },
{ name: "Css", status: false },
{ name: "Vue", status: true }
];
window.localStorage.setItem("todos", JSON.stringify(this.todoList));
}
};
</script>
<style scoped>
.content {
width: 800px;
height: 600px;
margin: 0 auto;
background-color: rgb(245, 245, 245);
padding: 24px 0;
}
.header {
font-size: 48px;
text-align: center;
}
.content-div {
font-size: 16px;
}
.add-content {
font-size: 16px;
margin-left: 15px;
padding: 10px;
}
.text_input {
width: 90%;
height: 30px;
padding: 10px 10px;
font-size: 14px;
border: 1px solid transparent;
}
.add_button {
width: 60px;
height: 30px;
}
</style>
Шаг 5
Код All.vue, Completed.vue и Uncompleted.vue в основном одинаков, за исключением того, что вычисляемое свойство немного отличается. Чтобы продемонстрировать использование маршрутизации, он разделен на три разные страницы.
<template>
<ul class="list">
<li class="item" v-for="(todo,index) in todos" :key="index">
<div>
<span class="item">{{index+1}}. {{todo.name}}</span>
<button v-if="todo.status" @click="changeStatus(todo.name)">已完成</button>
<button v-else @click="changeStatus(todo.name)">未完成</button>
</div>
</li>
</ul>
</template>
<script>
export default {
computed: {
todos() {
return JSON.parse(window.localStorage.getItem("todos"));
}
},
methods: {
changeStatus(name) {
const todos = JSON.parse(window.localStorage.getItem("todos"));
todos.forEach(todo => {
if(todo.name === name){
todo.status = !todo.status;
}
});
window.localStorage.setItem("todos", JSON.stringify(todos));
this.$router.go(0);
}
}
};
</script>
<style scoped>
.list {
list-style: none;
padding: 0px;
margin: 0px 25px;
}
.item {
list-style: none;
display: inline-block;
width: 90%;
padding: 5px 0px;
}
</style>
Вычисляется для файлов Completed.vue и Uncompleted.vue.
computed: {
todos() {
const todoList = JSON.parse(window.localStorage.getItem("todos"));
return todoList.filter(x => x.status);
}
},
vuex
Структура каталогов
ключевое знание
- как использовать vuex;
- Как получить недвижимость в штате;
- Как синхронизировать состояние изменения;
полный код
шаг 1
npm i vuex -S
Шаг 2
store.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
todos: [
{ name: "JavaScript", status: true },
{ name: "Css", status: false },
{ name: "Vue", status: true }
]
},
mutations: {
changeTodoState(state, name) {
state.todos.forEach(todo => {
if (todo.name === name) {
todo.status = !todo.status;
}
});
},
addTodo(state, todo) {
state.todos.push(todo);
}
}
});
export default store;
Шаг 3
Изменить app.js
.....
import store from './store.js';
.....
new Vue({
el: '#app',
store,
router,
render: h => h(App)
})
Изменить App.vue
<template>
.....
<p
class="content-div"
>当前共有{{this.$store.state.todos.length}}个代办事项,已完成{{completed}}个,剩余{{this.$store.state.todos.length-completed}}个。
</p>
.....
</template>
<script>
export default {
......
computed: {
completed() {
return this.$store.state.todos.filter(x => x.status).length;
}
},
methods: {
add() {
this.$store.commit("addTodo", { name: this.todo, status: false });
}
}
};
</script>
Изменить All.vue, Completed.vue, Uncompleted.vue
......
<script>
export default {
computed: {
todos() {
return this.$store.state.todos.filter(x => x.status);
}
},
methods: {
changeStatus(name) {
this.$store.commit('changeTodoState',name);
}
}
};
</script>
......
Суммировать
Пример, показанный в этой статье, относительно прост, поэтому объяснений не так много. Сделать это один раз гораздо полезнее, чем десять раз прочитать документацию.
Здесь есть все, если эта статья была вам полезна, ставьте ❤.
Добро пожаловать на общение~
моя колонка
Цепочка прототипов Javascript и наследование
Узнайте об асинхронном программировании Node.js в одной статье
Серия серверных разработок Node.js, посвященная быстрому началу реального боя с нуля