模仿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
}
}
}
})