前言
前几天,因为借着看源码的热乎劲,搞了一个玩具Js库Strview.js。为什么会搞这么一个玩具库呢?其实也不全是因为晚上闲的没事,主要还是想通过实操来锻炼自己的开发能力。之前,我也写过一篇文章,那篇文章只是大体介绍了一下,没有深究。之前大家可能觉得它跟Vue.js差不多,是的,正是借鉴Vue.js的思想,但是有些地方还是不一样(个人觉得)。所以,今天,这篇文章介绍基于Strview.js搭建的项目脚手架工具
StrviewApp。如果你觉得对自己有用,可以继续看下去。如果觉得这篇肯定是篇垃圾文章,你也可以避而远之。好了,我们现在就进去正题。准备好了吗?一起跟我来吧!
快速上手StrviewAPP
你可以通过StrviewCLI快速初始化StrviewAPP项目,你可以这样:
- 全局安装。
npm i strview-cli -g
- 安装完成之后,你可以查看版本。
strview-cli -v
- 最后,就是初始化项目了,
<projectName>
是自定义项目名称。
strview-cli init <projectName>
or
strview-cli i <projectName>
这样,一个StrviewAPP项目就这么搭建完成了。
StrviewAPP项目结构
下图就是StrviewAPP项目组织结构。
下面,我将介绍每个文件及文件夹的作用。
- config
这个是webpack配置文件夹,关于webpack的配置都在这配置。文件夹中里面有如下三个文件,分别如下:
- webpack.base.js // 基础配置 - webpack.dev.js // 开发环境配置 - webpack.pro.js // 生产环境配置
- public
资源文件夹。
- favicon.ico // 网站标识 - index.html // 模板文件
- .gitignore
哪些文件不需要添加到版本管理中。
- .prettierrc
Prettier 规则配置文件。
- package.json
定义了这个项目所需要的各种模块,以及项目的配置信息(比如名称、版本、许可证等元数据)。
- src
这个文件夹是StrviewAPP项目的主要文件夹,下面我们来看下这个文件夹里面到底有什么。
- assets //存放静态文件 - components // 组件文件夹 - data // 公用状态文件夹 - methods // 方法文件夹 - style // 样式文件夹 - template // 模板文件夹 - App.js // 页面入口 - main.js // 项目入口文件
Src文件夹详析
上面我们分析完了项目结构,那么下面我们将进一步分析Src文件夹中的文件构成以及它们之间如何配合的。
1. main.js
首先,我们看下main.js
文件,这是项目入口文件,我们来看下文件中的内容。
import { createView } from 'strview'; import data from './data'; import App from './App'; import methods from './methods'; createView({ el: "#app", template: App, data }); // The event is handled after the createview API methods();
我们先引入了strview.js
,导入createView
这个API用于创建视图。那么,我们我们跳到下面看下这个API是怎么使用的。首先我们传入一个对象字面量,第一个属性是el
属性,它是挂载的DOM节点。第二个属性是template
属性,它是用于显示视图的模板。第三个属性是data
属性,传入值为显示的数据。最后,我们看到有这么一行注释The event is handled after the createview API
,意思是事件方法必须要在createView
API之后调用,即这里的methods();
。
2. App.js
上面说到,App.js
用与显示视图的模板,那么下面我们来看下。
import myParagraph from './components/myParagraph'; import card from './components/card'; import helloTemplate from './template/helloTemplate'; import './style/index.css'; const App = ` ${helloTemplate} <div class="content"> <button class="color-red">点击</button> <p class="txt">{a},{b},(a和b都改变)</p> <ul class="list"> <li>{age}</li> <li>{name}</li> <li>{msg}</li> </ul> <p class="txt">{a},(a会改变)</p> <p class="txt">{b},(b会改变)</p> <input value="{msg}"></input> <p>{obj.a.b}</p> <p>{arr}</p> <p>{ob.name}</p> </div> ${myParagraph} ${card}<my-card><span slot="my-card-txt">{b}</span></my-card> ` export default App
我们看到在代码的末尾导出了一个模板字符串,也就是常量App
。我们可以看到模板字符串中都是些类似标签语句的代码。是的,这也是Strview.js
的关键之处,使用含有类似标签语句的模板字符串来构建视图。
另外,我们看到顶部除了引入样式文件,还从components
文件夹引入了两个文件,template
文件夹中引入了一个文件。我们从前面目录结构知道,components
文件夹存放的是组件,而template
文件夹存放的是模板文件。如何将导入模板与组件呈现到页面上呢?那么就需要在模板字符串中使用${}
占位符。在这里你可能会感到很困惑,因为没有看到这些文件中什么内容,不过不要着急,我们慢慢来。你在这里只需要记住它们在这里占位就可以了。
你可能会看到<my-card></my-card>
这种标签,你可能说没见过!这只是一个自定义组件。具体为什么这样写,我们下面到组件时再分析。但是需要说明的是,如果我们组件中需要存放内容时,我们需要在自定义组件前使用一个占位符${}
,如这里的${card}
,card
是引入的组件。
最后,我们在标签中都会发现类似这种{}
符号,它是用来挂载数据的,也就是为了动态更新数据的。数据这块我们后面再细讲。
3. template
上面说到,这个文件夹是存放模板文件的,我们就一探究竟。
- helloTemplate.css - helloTemplate.js
helloTemplate.css样式文件没有什么好说的。
.container { text-align: center; margin-top: 100px; line-height: 46px; } .container > img { margin-bottom: 40px; }
helloTemplate.js我们来看下这个js文件。
import logo from '../assets/logo.png'; import './helloTemplate.css'; export default ` <div class="container"> <img src="${logo}"/> <h1>Hello Strview.js</h1> </div> `;
在上面代码中可以看到我们头部引入了一个图片还有一个样式文件,下面接着导出了一个模板字符串。 引入的图片呢!使用${}
占位符来绑定到img
标签上。
简单介绍下template
文件夹之后,我们下面看下components
文件夹。
4. components
这个文件夹的是存放组件的,组件这个概念大家可能非常熟悉,在目前Vue、React这些前端框架中都有相应的应用。
我们先来看下这个文件夹的目录结构。
- card.js - myParagraph.js
可以看到,有两个js文件。
先看myParagraph.js
这个文件。
customElements.define('my-paragraph', class extends HTMLElement { constructor() { super(); const template = document.getElementById('my-paragraph'); const templateContent = template.content; this.attachShadow({ mode: 'open' }).appendChild( templateContent.cloneNode(true) ); } } ); const myParagraph = `<template id="my-paragraph"> <style> p { color: white; background-color: #666; padding: 5px; } </style> <p> <slot name="my-text">My default text</slot> </p> </template> <my-paragraph> <span slot="my-text">Let's have some different text!</span> </my-paragraph> <my-paragraph> <ul slot="my-text"> <li>Let's have some different text!</li> <li>In a list!</li> </ul> </my-paragraph>` export default myParagraph
我们先看上一部分,customElements
对象下有一个define
方法。这是什么方法呢?
其实这部分利用了Web Components。它是什么呢?我们在MDN这样定义它的。
Web Components 是一套不同的技术,允许您创建可重用的定制元素(它们的功能封装在您的代码之外)并且在您的web应用中使用它们。
Web Components拆开来讲其实也挺复杂,我们在这里就不详细分析了。以下是MDN网址,大家可以跟着做几个例子。
https://developer.mozilla.org/zh-CN/docs/Web/Web_Components
我们在这里是需要知道define
方法第一个参数需要传一个自定义标签名,第二个参数是传入一个类。需要自定义的地方是第一个参数与第二个参数中getElementById()
方法中的参数,推荐使用相同的字符串。
调用define
方法完成后,你需要在下面模板字符串中首先要使用template
标签包裹起来,你可以理解成初始化。我们可以看到在template
标签上有一个id选择器与上面的getElementById()
方法中的参数一样。是的,这地方必须一一对应。另外,我们看到紧接着下面有一个style
标签,这是定义组件样式的。最后就是组件的内容了。这里定义了一个p
标签,里面是一个插槽,定义了一个name
属性。并且这里有一个标签文本,这个文本内容是默认显示的,如果组件中没有内容,则这个内容就会默认显示。
<template id="my-paragraph"> <style> p { color: white; background-color: #666; padding: 5px; } </style> <p> <slot name="my-text">My default text</slot> </p> </template>
我们接着看下面代码,它们都是用<my-paragraph></my-paragraph>
包裹起来。另外,标签里面则是普通的标签语句。但是,有一点不一样的是,这些普通的标签语句都有一个slot
属性,这个属性用于当作插槽的模板。
<my-paragraph> <span slot="my-text">Let's have some different text!</span> </my-paragraph> <my-paragraph> <ul slot="my-text"> <li>Let's have some different text!</li> <li>In a list!</li> </ul> </my-paragraph>
分析完了myParagraph.js
文件,我们接着分析card.js
文件。
其实与myParagraph.js
文件一样,只不过它是负责定义组件。在上面的App.js
中,我们提到我们需要在自定义组件前使用一个占位符${}
,如这里的${card}
,card
是引入的组件,就是指的它。
customElements.define('my-card', class extends HTMLElement { constructor() { super(); const template = document.getElementById('my-card'); const templateContent = template.content; this.attachShadow({ mode: 'open' }).appendChild( templateContent.cloneNode(true) ); } } ); const card = `<template id="my-card"> <style> div { color: #333; background-color: #f4f4f4; padding: 5px; } </style> <div> <slot name="my-card-txt"></slot> </div> </template> ` export default card
5. data
这个文件夹是负责存放数据状态的文件,里面主要有这两个文件。
- index.js - ob.js
我们先来看下index.js
文件,非常简单,就是单纯的导出一个对象,另外ob.js
文件也是。
index.js
import ob from './ob'; export default { a: "Hello", b: 18, name: "maomin", age: 9, msg: 'Strview', arr: ['0'], obj: { a: { b: 1 } }, ob }
ob.js
export default { name: 'kk' }
6. methods
我们在main.js
文件中中提到一点。
import methods from './methods'; // The event is handled after the createview API methods();
就是调用这个方法。那么,我们下面看下这个methods
文件夹,我们知道这个文件夹的作用是提供事件处理方法的。它的目录结构如下:
- index.js - item.js
先来看下item.js
这个文件。
import { reactive, ref } from 'strview' function executes() { reactive().obj.a.b = 3; ref().name = 'Strview.js'; } function useItem() { ref().b = 100; } export { executes, useItem }
我们可以看到在头部引入了两个方法,reactive
、ref
这两个方法前者负责处理复杂类型的数据,如数组、嵌套对象,后者处理简单类型的数据,如单一对象、原始值。可以看到在上面代码我们通过调用reactive()
、ref()
这两个方法来实现数据的响应式,然后导出这两个executes()
、useItem()
方法。
接着,我们来看下index.js
文件。
import { eventListener } from 'strview'; import { executes, useItem } from './item'; const eventList = [ ['.color-red', 'click', executes], ['.list>li:nth-child(2)', 'click', useItem] ] function methods() { for (let index = 0; index < eventList.length; index++) { const element = eventList[index]; eventListener(...element); } } export default methods
我们首先在文件顶部引入了一个eventListener
方法,然后接着从item
文件夹引入的之前导出的两个方法。通过定义一个数组,来不断地循环调用eventListener
方法。
最后导出methods
方法。
7. style
这个是存放样式的文件,不过多介绍了。
index.css
* { margin: 0; padding: 0; font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } .content { text-align: center; margin-top: 50px; }
8. assets
这个文件夹存放的是静态资源,比如图片之类的资源。
项目启动
- 初始化安装依赖
yarn install
OR
npm install
- 启动项目
yarn start
OR
npm run start
- 打包部署
yarn build
OR
npm run build
项目一览
结语
谢谢你的阅读!
这个脚手架相比于现在热门的前端框架中的脚手架肯定是没有可比性,可以当做是玩具吧!也可以当做自己看源码之后自己的一些感悟吧!