一、工具

  • 具备html、css、js的基础知识
  • chrome浏览器
  • vscode文本编辑器
  • vuejs的官方文档

二、我理解的vuejs

  • vue(读/vju:/,与view同音)是一套用于构建用户界面的js框架,它是轻量级的,易上手(作为一个前端小白本人用事实证明确实易上手)

  • 官方提供了webpack等脚手架工具,提供了一整套前端集成框架,所有依赖都已经包办,只需要下载好然后根据要求在指定目录下开发自己的项目内容即可(但本博文没有使用此方法)

  • 另一种引入vue的方法是,直接在.html静态文件中通过<script>标签的方式引入(本文采取的方式),可以根据需要选择版本,这仅适用小小型应用:

    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
  • 我理解的vue是一个写好的js类,我们需要先new一个vue的实例并绑定标签,才能使用它

  • vue能够动态地将html中的元素与vue中定义的data双向绑定起来,动态更新,能够方便地将数据操作与页面渲染展示的工作分割开来,能更专注于展示,屏蔽底部细节。下面的介绍也会体现这一点,这里仅概括一下我的理解

三、编写一个简单的todolist

3.1 引入vuejs
  • html部分的静态文件非常简单,如下所示:

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8" />
            <title>TodoList</title>
            <link href="index.css" rel="stylesheet" type="text/css" />
            <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
        </head>
        <body>
            <div id="app">
                <todo-body></todo-body>
            </div>
            <script src="../src/main.js"></script>
        </body>
    </html>
  • 注意到<body>部分有一个<todo-body>的标签,这是一个vue组件(component),正确定义和注册就可以直接在html文件中以标签的形式使用

  • 引入vuejs之后,在chrome浏览器中可以观察到文件目录结构如下所示:
    文件目录结构

3.2 注册一个vue的实例,并绑定id
// main.js
new Vue({
    el: '#app', 
})
  • 这里的elelement的意思
  • #app表示id="app"的元素是一个vue实例
  • 缺少这一句注册,是无法正确渲染的
  • 曾尝试过将el设为'body',会报错说不要将vue挂载在<html><body>元素上,让我使用一般的元素:
[Vue warn]: Do not mount Vue to <html> or <body> - mount to normal elements instead.
3.3 注册一个vue组件
(1)什么是vue组件
  • 任何应用界面都可以抽象为一个组件树(图源自vuejs官方文档):
    组件树

  • 在vue中,组件的本质是一个拥有预定义选项的vue实例,因此组件也拥有vue的四大选项:data数据、methods方法/函数、watch监听事件、props组件自定义属性

(2)注册一个名叫todo-body的vue组件
//main.js
Vue.component('todo-body', {
    data: function() {
        return {
            title: 'This is a ToDoList'
        }
    },

    template: '<h2>{{ title }}</h2>',

    methods: {},
    watch: {}
})
  • 意思是注册一个名叫todo-body的vue组件,其拥有一个数据变量title(一个拥有初始值的字符串)
  • 这样注册以后,在静态html中使用<todo-body>标签就会套入组件中定义好的template部分(如3.1所述),而template中的双花括号会显示组件中的title变量的值
  • 需要注意的是,注册组件用到的data必须是以函数返回值的形式,避免复用组件时因数据共享导致的意想不到的错误
3.4 完善todo-body的vue组件
//main.js
Vue.component('todo-body', {

    data: function() {
        return {
            title: 'This is a ToDoList',
            newLabel: '',
            items: []
        }
    },

    template: '<div>\
    <h2>{{ title }}</h2> \
    <input v-model="newLabel" v-on:keyup.enter="addNewLabel"> \
    <ul> \
        <li v-for="item in items" \
            v-bind:class="{finished: item.isFinished}" \
            v-on:click="finishState(item)" \
            style="cursor:pointer"> \
        {{ item.label }} \
        </li> \
    </ul> \
    </div>',

    methods: {
        addNewLabel: function() {
            this.items.push({
                label: this.newLabel,
                isFinished: false
            });
            this.newLabel = '';
        },
        finishState: function(item) {
            item.isFinished = !item.isFinished;
        }
    },

    watch: {}
})

(1)首先,一个组件的template只能包含一个根标签,本例是<div>

(2)template的根标签中包含三个子标签:<h2><input><ul>,分别表示标题、输入待办事项的输入框、待办事项列表

(3)data部分新增两个变量:newLabel用于接收输入的新的待办事项,初始值为空字符串;items用于存储当前已经输入的所有待办事项,初始值是一个空数组,数组的每一个元素都是一个json格式的数据,包括labelisFinished两个字段

(4)下面重点解释一下template中的vue指令(可以参考官方文档

  • v-model="newLabel":表示<input>输入框中输入的内容在回车后直接赋值给newLabel
  • v-on:keyup.enter="addNewLabel":表示当触发回车键时,将会调用组件的methods中定义好的addNewLabel函数
  • v-for="item in items":表示循环取出items数组中的每一个元素item,以列表的方式渲染item.label的内容,如果数组为空则不渲染<li>标签
  • v-bind:class="{finished: item.isFinished}":根据双引号中的表达式的布尔值决定是否使用class=finished属性(配合css使用)
  • v-on:click="finishState(item):表示当触发click事件时,将会调用组件的finishState(item)函数
3.5 使用localStorage存储待办事项
  • window对象表示浏览器的一个实例,它既是通过js访问浏览器窗口的一个接口,又是ECMAScript规定的Global对象

  • localStorage是浏览器提供的一个本地存储,其文件存放在浏览器目录下的某个文件夹中,只要不去手动删除它,每次刷新浏览器页面这个文件都还是存在的,那么我们就可以通过它存储我们刚输入的待办事项

  • localStorage有两个方法:setItem(文件名, 存储内容)用于设置保存内容到本地存储,getItem(文件名)用于提取本地存储的内容

    //main.js
    const STORAGE_KEY = 'todo-list';
    
    function initalItems() {
        return JSON.parse(window.localStorage.getItem(STORAGE_KEY) || '[]')
    }
    
    function saveItems(items) {
        window.localStorage.setItem(STORAGE_KEY, JSON.stringify(items))
    }
    
    Vue.component('todo-body', {
    
        data: function() {
            return {
                title: 'This is a ToDoList',
                newLabel: '',
                items: initalItems()
            }
        },
    
        template: '...',
        methods: {...},
    
        watch: {
            items: {
                handler: function(items) {
                    saveItems(items);
                },
                deep: true
            }
        }
    })

    这里的代码增加了两个函数用于设置和提取本地存储内容,并修改了vue组件中的两个地方:

  • 数据变量中的items的初始值不再是空数组,而是从本地浏览器中提取到的内容,如果提取不到则设为空
  • watch监听items变量的变化,只要items发生变化,就会调用handler函数,将变化后的items存储到localStorage,并且是深复制(类似于python的深复制机制)
  • localStorage的内容可以在chrome浏览器中查看,如下图所示:
    localStorage

  • 这样一来即使关掉浏览器本地存储的内容也还会存在,再次打开使用浏览器打开该html时,依然能正确加载先前存储好的待办事项。如果想要清空,只需要选中localStorage下对应的html文件目录,右键,clear即可

四、未解决问题记录

main.js调用store.js中定义的函数:

//main.js
new_element = document.createElement("script");
new_element.setAttribute("type", "text/javascript");
new_element.setAttribute("src", "../src/store.js"); //引入store.js
document.body.appendChild(new_element);

然后出现了store.js中的函数能在main.js的vue组件中的watch中调用,但不能在data中调用的情况,报错为initialItems未定义。

疑似与vue的生命周期有关,查阅一些资料可以使用createdmounted,似乎无法解决上述调用问题,因此直接将store.js的函数写在main.js中了,仍未能解决js相互调用函数的vue渲染问题。

Logo

前往低代码交流专区

更多推荐