如何手动实现一个Vue 以及 如何在浏览器上运行ES6项目

本文详细介绍了一个模仿Vue及其项目目录结构的ES6项目实践,包括使用Webpack进行项目打包,解析Vue实现原理,并通过个人学习网易云课堂内容,构建了一个可运行在浏览器的ES6项目。项目涉及的主要技术包括Webpack、Vue原理、ES6语法等。

模仿Vue以及Vue项目目录,实现一个可运行在浏览器的ES6项目

主要是webpack打包工具,以及vue实现的原理

vue原理部分,个人学习自网易云课堂

项目地址:

https://github.com/lovefive5/vue_es6.git

项目目录:

效果图: 

webpack 打包配置

const path = require('path')  //引入path
const HTMLPlugin = require('html-webpack-plugin')

module.exports = {
    mode: 'production',
    entry: './src/main.js',
    output: {
        path: path.resolve(__dirname, 'dist/'),
        filename: 'bundle.js'
    },
    module: {
        rules: [
            {
                test: /\.styl$/,
                use: [
                    'style-loader',
                    'css-loader',
                    {
                        loader: 'stylus-loader'
                    }
                ]
            },
            {
                test: /\.css$/,
                use: [
                    {
                        loader: 'style-loader',
                        options: {
                        }
                    },
                    {
                        loader: 'css-loader',
                        options: {
                            modules: false
                        }
                    }
                ]
            },
            //图片文件打包loader
            {
                test: /\.(jpg|jpeg|png)$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            //placeholder 占位符
                            outputPath: 'images',
                            name: '[name]_[hash].[ext]',
                            limit: 1048 * 2 
                        }
                    }
                ]
            }
        ]
    },
    plugins: [
        new HTMLPlugin({
            title: "webpack-study",
            filename: 'index.html',
            template: './public/index.html'
        })
    ],
    devServer: {
        contentBase: path.join(__dirname, 'dist'),
        port: 3000,
        hot: true
    }
}

 Vue实现

import Watch from './watch'

/**
 * 封装 Vue 类库
 * 实现双向绑定,以及函数调用
 * tips:函数调用肯定有坑啊
 */
class Vue {
    constructor(options) {
        this.options = options //实例化的 vue 配置
        this.$data = this.options.data //获取配置中的数据
        this.$el = document.querySelector(this.options.el) //获取指定节点的 Dom 对象
        this._observerable = {} //观察者容器
        this.$command = this.Command() //Vue默认提供的指令集合
        this.$methods = this.options.methods //
        this.Observerable(this.$data) //进行数据劫持处理
        this.Complite(this.$el) //进行指令处理
    }
    /**
     * Vue 进行数据劫持
     * @param {*} data 
     */
    Observerable(data) {
        for (let key in data) {
            this._observerable[key] = [] //指定观察者容器存储的类型为数组,即一个指令(v-text)的所有观察者

            let val = data[key] //获取对象中的值

            const watch = this._observerable[key]//获取观察者对象数组
            /**
             * 双向绑定的原理 , 修改对象上的属性
             * 相当于Java中的 setter getter
             */
            Object.defineProperty(this.$data, key, {
                //取出对象中的值
                get() {
                    return val
                },
                //设置对象里的值,此处实现自定义方法
                //每当设置值时,都会对已存储的 watch 进行遍历,然后调用Watch对象的update方法,从而修改Dom
                set(newVal) {
                    if (newVal !== val) {
                        val = newVal
                        watch.map(item => item.update())
                    }
                }
            })
        }
    }
    Complite(ele) {
        //获取#app下面的子节点
        const nodes = ele.children
        //对子节点进行遍历,找到含有指令的标签
        Array.from(nodes).map(element => {
            //如果当前遍历出的字节点还有子元素。则递归查找
            if (element.children.length > 0) {
                this.Complite(element)
            }

            this.$command.map(item => {
                //获取指令对象
                const val = element.getAttribute(item.command)
                if (element.hasAttribute(item.command)) {
                    if (item.type != 3 && this._observerable[val] === undefined) {
                        console.error("设定的:" + val + " 不存在")
                        return;
                    }
                    switch (item.type) {
                        case 1:
                            this._observerable[val].push(new Watch(this, element, val, item.operating))
                            break
                        case 2:
                            this._observerable[val].push(new Watch(this, element, val, item.operating))
                            //设置input事件,实时更新 vue-data 中指定指令的值
                            element.addEventListener('input', () => {
                                this.$data[val] = element.value
                            })
                            break
                        case 3:
                            //重新绑定 this 对象
                            const fun = this.$methods[val].bind(this.$data)
                            element.addEventListener('click', fun)
                            break
                    }
                }
            })
        })
    }
    //默认的指令集合
    Command() {
        return [
            {
                command: 'v-html',
                operating: 'innerHTML',
                type: 1
            },
            {
                command: 'v-text',
                operating: 'innerText',
                type: 1
            },
            {
                command: 'v-model',
                operating: 'value',
                type: 2
            },
            {
                command: 'v-on:click',
                operating: '',
                type: 3
            },
        ]
    }
}

export default Vue

Watch 观察者

/**
 * 观察者,
 * 通过数据变更,更新页面对象
 */
class Watch {
    /**
     * 
     * @param {*} vm vue实例对象 
     * @param {*} el 标签元素
     * @param {*} exp 指令
     * @param {*} val 指定对应的操作
     */
    constructor(vm, el, exp, val) {
        this.vm = vm
        this.el = el
        this.exp = exp
        this.val = val
        this.update()
    }
    update() {
        //Dom操作 input[value] = 'hello world'
        this.el[this.val] = this.vm.$data[this.exp]
    }
}

export default Watch

项目调用:

import Vue from './controller/vue'
import "./assets/style/main.css"

new Vue({
    el: "#app",
    data: {
        username: "",
        password: "",
        input_tips: ""
    },
    methods: {
        check() {
            this.input_tips = ''
            if (this.username == '' || this.username.length < 6) {
                this.input_tips = "<p style='color:red;'>账户名输入不规范</p>"
                return
            }
            if (this.password == '' || this.password.length < 6) {
                this.input_tips += "<p style='color:blue;'>密码输入不规范</p>"
                return
            }
        }
    }
})

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Auspicious5

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值