工程化
这里要从node.js开始。
-
node.js
要弄一个基于事件驱动、非阻塞I/O的的web服务,
发现V8引擎+JavaScript很合适。
这样Js就能直接写后端应用。
然后发展成作为本地的运行容器(类似jdk),将js导入到了本地运行领域。
以此位基础,发展出了生态环境,里面关系挺绕的,各种轮子,互相占位。 -
nmp/yarn
类似maven的包管理。下载各种包,处理依赖、版本。
yarn改进了旧版的nmp。 -
webpack
Webpack是一个模板,给前端的资源加载/打包
处理模块关系,这些复杂依赖关系的静态文件(js,cs)打包成一个或很少的静态文件。
提高兼容性,可以将一些浏览器尚不支持的新特性,不能直接运行的拓展语言(Scss,TypeScript)转换为可以支持格式.
这里为啥要打包呢。还是以前这样松散在那里有啥不好。而且打包出来也是非常多个chunk-xxx.js文件呀。
- vue-cli-service
在webpack上又包装了一层。自动创建相关的目录结构,初始化适合VUE的默认配置。
然后下载来的的依赖模块node_modules中非常多。
打包出来的文件, chunk-vendor.js特别大,保存各种相关依赖,elmentUI之类的组件库。VUE本身?
page下面的一个页面是单独生成一个js,然后components里的组件是嵌入到调用页面的js
html是没有的,都是js里面生成。
dist下面有个report.html详细记录了某个js里打包了哪些模块。
感觉代码是运行在托管平台上。出了问题后都搞不清楚咋回事。。
安装使用
-
node.js
选了安装包形式。勾选了个auto选项,
下载的一堆东西,连vs的编译器都下载来,是现场编译的么?看着很复杂。
接下来的模块npm/webpack等等都是JS脚本,依赖于node.js环境执行 -
nmp
node安装后自带的
这里可能会遇到路径问题
npm install lodash
当前文件树中的 node_modules 子文件夹下
npm install -g lodash
使用全局的位置,npm root -g 命令会告知其在计算机上的确切位置。
yarn/WebPack这种就是用npm -g 安装到全局目录 -
Webpack
安装--npm install webpack -g
输入webpack-提示安装webpack cli啥的都yes。 -
npm项目/打包
先要弄成一个npm项目。
转到项目所在文件夹。
npm init ,生成一个项目环境。
package.json
node_modules,dist文件夹
把runoob1.js文件放在node_modules中。
这里npm和webpack可以交互。
可以在 package.json-"scripts": 节中加入webpack --mode=development runoob1.js
用npm run build。间接并带参数调用webpack运行。
webpack也是读取package.json中的信息。
执行 webpack --mode=development runoob1.js
网上说webpack.config.js里配置mode:'development'。
这个文件好像是要自己建的。
生成的文件在dist文件夹中。
这个只能打包JS文件,打包css文件要再下载插件。
用-g全局下载,貌似会找不到,只能下到当前目录。
然后用参数打包,会越来越复杂,编一个打包配置文件。
大致打包的总体规则由webpack.config.js设置。
里面具体的依赖规则。 写在 entry: "./runoob/runoob1.js"
随着规范的更新,多了很多标记
script defer 异步延迟下载
//<link href=js/chunk-vendors.f025029c.js rel=preload as=script>
先是基本的配置
package.json--npm项目生成的配置
webpack.config.js--webpack的配置
然后拓展成多种设定,类似于debug,release。
这里看着是配置能继承,定义基础配置,然后在上面继承,再定义差异部分。
package.json-"scripts": {
"dev": "node build/dev-server.js",
"start": "node build/dev-server.js",
"build": "node build/build.js"
},
用了脚手架后,
webpack--被切成好多块
先是build/build.js-->webpack.config.js-->config/index.js(看着主要是一些路径)??
配置应该是在node_modules@vue\cli-service\这里
然后也是绕来绕去的。
lib\Service.js 应用了options.js
lib\optiongs.js 设置了默认的参数
app.js中调用webpackConfig
- vue-cli-service
有两个独立的部分- vue/cli--
直接在cmd里输vue执行。
用yarn global add @vue/cli后,找不到命令,文件是在C:\Users\Administrator\AppData\Local\Yarn\bin下面,可以手动添加到环境变量。
这个可以用来新建一个项目。
还有vue ui 可视化查看和配置项目。。 - vue/cli-service
这个是在每个项目下面。
用yarn vue-cli-service 运行 - 运行
vue-cli-service serve
这时候应该也是生成页面了的,但不知道目录在哪。 - 发布
vue-cli-service build
生成的都是静态页面了。
nginx部署--直接复制的html下面,
nodes部署--不清楚。
- vue/cli--
node使用
这有点类似手机发展的过程,原先只是为了通讯打电话。
直到装了一个系统,变成了一个通用平台,通话只是其中一项功能。
这个装了V8,可以运行JS后,再给加上组件使其能访问本地资源。
http只是其中一个模块了。
- 命令行
cmd->node进入交互环境,直接输命令。 - 运行脚本
hello.js
console.log('Hello, world.');
node hello.js
这个不像其它语言有个入口函数main()这样,就从上到下。
- http服务
var http = require('http');
var server = http.createServer(function (request, response) {
// 回调函数接收request和response对象,
console.log(request.method + ': ' + request.url);
response.writeHead(200, {'Content-Type': 'text/html'});
response.end('<h1>Hello world!</h1>');
});
server.listen(8080);
console.log('Server is running at http://127.0.0.1:8080/');
url路由这些就在这入口再去扩展。
- 脚本之间调用
遵循CommonJS规范。模块化
module.exports / require (xx.js) .和ES6的语法没啥关系的-
exports
大部分只有一个输出,
匿名函数? module.exports = function (opts) {}
或者 function greet()
module.exports = greet;导入 var msg = require('./Log.js'); msg('Hello World');
-
打包调试
打包
@vue/lib/Service.js--给WebPack做大量配置后执行
这里就是这么多module是怎样配合的。
module有些是单纯js,有些是npm工程.
代码层面一种是使用cmd:node xx.js;一种是requier进来.
这里的npm工程不知道存在哪几种情况。
自建的例子一般都是node去调用,并没有给第三方调用的接口
调试
应该也是先打包,只是打包到内存里。用webpack-dev-service来做服务器。
这个服务器和浏览器建立WebSocket通讯,代码有更新就随时通知浏览器刷新。
vue
文档分版本的,不同版本写法不一样。
vue本身分2.x,3.x
下面的组件也分版本如-vue router 3.x,4.x。
看文档的时候要先选对。
就只有pbulic\index.html是原来的风格,src里面就都是另外的语法了。
也有点像你按传统的Java写代码,引入spring后,里面的语法,逻辑也是完全蒙了。。
就只剩下引入时的代码还是传统的。
几个方面吧
MVVM--视图和数据双向绑定
Component-页面组件化。
不管是页面,还是一个dailog,还是一个elmenent。都先包装成component。
通过component的名称放到页面来。
Router--路由管理
State--状态管理
主体流程。看着就是页面元素抽象为一个树形结构。。然后不断的循环结构,建立Dom元素
vue-arch
- 入口流程
- 入口-main.js
import App form './App' R_A
vue 1.x的写法
new Vue{
el:'#app' R_B
components:{App} R_A
}
vue2.x的写法
new Vue({
router, --一个VueRouter类型的实例。
render: h => h(App), R_A
}).$mount('#app') R_B
这个h说是createElement的简写。
- 主组件App.vue R_A
<div id="app"></div> R_B
使用其他组件来搭建页面
<CompA> </CompA>
import CompA from './path/CompA.vue'
export default{
components:{CompA} --这里对外申明使用了哪个组件??
}
<router-link to="/foo">Go to Foo</router-link> /foo 链接路径
<router-view></router-view> --链接页面的容器
-
主页面 public\index.html
说是webpack会以这个为启动页面,资源链接都打包进去。
要用到的js脚本都是在这儿引入。 -
引入第三方库/组件的流程
dependencies添加引用
一种是引入单个组件
import VueDragResize from 'vue-drag-resize'
Vue.component('vue-drag-resize', VueDragResize)
一种是引入组件库
import ElementUI from 'element-ui';
Vue.use(ElementUI);
说是use里面有个install方法,集中调用Vue.component。
然后里面还可以加各种逻辑啥的。
- 路由router/index.js
把建VueRouter实例独立出来。
引入链接页面
import Foo from '../components/FooComp.vue';
链接规则
routers
[{path:'/foo',component:Foo}] --路径对应的页面
const router = new VueRouter({
routes
})
嵌套的router
在一个页面里,可以有自己的 <router-view/>
然后规则在此页面的路由规则中的children: [{}]
url栏这里后面会加入“/#”+路由名 ,启动时是“/#/”,调用到‘/’的路由。
这里怎么控制url显示的?
参数路由
{ path: '/user/:id', component: User }
这样所有不同ID的用户都会映射到同一个路由,如/user/foo 和 /user/bar
从 /user/foo 导航到 /user/bar,
原来的组件实例会被复用。组件的生命周期钩子不会再被调用。
目标页面通过
- $route.params.id
- props: {
username: String,
password: String
},
路径规则
绝对路径-前面有/
相对路径-没有/,直接写字符。
还有一些通配符,正则表达式。
这里就算是子路径,也能写成/tag,url规则上看不出来子路径。
路由跳转/redirect
name: 'nbLed',
path: '/nbLed',
component: () => import('@/pages/led/NBLed'),
redirect: 'list',
children: [{
name: 'nbList',
path: 'list',
component: () => import('@/pages/led/nb/Index')
},
...
name: 'led',
path: '/led',
component: () => import('@/pages/led/Led'),
redirect: '/led/lora',
NBLed.vue中
<el-menu-item index="1" @click="toUrl('/nbLed/list')">{{$t('led.menu.sblb')}}</el-menu-item>
<keep-alive>
<router-view></router-view>
</keep-alive>
NB单灯的菜单路径是/nbLed/list,打开后是即打开了NBLed,然后又给里面的router-view跳转到/nb/Index页。
但是url这里直接输/nbLed会报404..
url直接输/led又是能跳转到/led/lora
nbLed应该是redirect: 'list'不对,要写完整路径 path: '/nbLed/list'
然后home页这里就已经跳转了。
作用就是把各层级的页面依次打开。
路由的一些要素
挂载点,路由标签,组件。
把组件加载到挂载点上。
页面结构上有层级,lay0/lay1/lay2
这样挂载点和标签都是一个层级结构
拿到一个标签后,就需要在层级机构中回溯对应。
回溯到最顶层,挂载到lay0,再下来一层层对应。
然后处于中间标签的地方可以用redirect下层的路径来路由一个默认页面
扩展一下
标签携带参数
前后加入预处理
lamp系统中
上面说的启动时是“/#/”,调用到‘/’的路由。
这里是顶层路由定义了 name: 'index', path: '/',
然后通过“router.berforeEach”再分配到登录界面或者其它。
router.matcher = new Router({
routes: constRouterMap
}).matcher;
说是router没法删除,要通过matcher来重置。
但这里constRouterMap和原来是一样的。
应该是登录以后router已经加入了动态路由,
这里是为再次登录先清理router。
逻辑上是分多个层次,
一是第一级的路由,到Login,BigScreen,lampmap。还有一些不清楚的页面也不知道怎么进去。
一个是核心home页面的路由,里面包含菜单的路由。
登录后才创建。
子路由
通过Menu api获取后加入菜单路由-- routes.push(route)..
建立home路由并加入菜单路由
router.addRoutes([{
name: 'home',
path: '/home',
component: () =>
import ('@/pages/Home'),
redirect: redirect,
children: routes
}])
home页面里包含菜单等基本模板,再放一个<router-view />.
一个是菜单路由下面的子路由
asyncRouterMap路由规则里面
path: '/device/:id',
redirect: '/led/lora',
children: [{}]
这个一般是放在tab控件里面切换页面?
led.vue页面有个menu tab组件可以切换
<keep-alive>--说是可以保持组件状态,可能是保持用户的输入项。
<router-view></router-view>
/keep-alive>
路由过滤
router.berforeEach{
根据登录情况,可以重定向到登录url。
}
参数路由
{
name: 'third',
path: '/third/:username/:password',
props: true,
component: () => import('@/pages/Third')
}
用来给第三方登录用,怎么接收参数?放在一个段落里,是新的段落标签,还是props标签。
是props标签中。
启动认证流程 待完善
main.js中
localStorage.getItem("isLogin")
isLogin addRouter
else router
- 状态管理vuex/store
需要引入vuex,app初始化时要加入。
状态响应-看例子就是维护一个state: {xx,xx}的状态变量。
这个变量不能直接赋值,要通过包装的方法来操作。
可能全局变量随意赋值,这个变量会搞不清楚在哪里被改变了。显式来操作比较好跟踪。
页面元素再绑定到这个state里面的值。 跟着变化。
大部分时候状态是绑定本地变量的,这里可能是跨页面的操作?
也是组件之间交互的核心。
一是数据交互,
一是动作交互。
一个组件通过watch状态值,触发相应动作。
另一个组件改变这个状态值,来触发watch。
相当于一个消息处理机制。
这里状态变量操作也比较繁琐。
定义状态变量
state: {loraMemu: '1'}
定义操作方法。
mutations:{
setLoraMemu(state, val) { state.loraMemu = val },
}
调用操作
this.$store.commit('setLoraMemu', { val: 10})
又多了包装了一层,说是可以异步调用。
看着就是写了下commit..
actions:{
setLoraMemu(context, val) { context.commit('setLoraMemu', val) },
}
调用action
this.$store.dispatch('setLoraMemu', '4')
状态读取
直接读取
this.$store.loraMenu
定义getter
getter:{
getLoraMenu(){
return state.loraMenu
}}
怎么调用?
再可能是为了简化写法?map系列
引入
import { mapState,xxx} from 'vuex'
读取mapState
mapState(['loraMemu']) --名字不用改用数组。
mapState({ --可以改名字或者加操作
loraMemu:'loraMemu',
loraMemu:state) => state.loraMemu --不同写法
})
mapAction([]) 和上面类似。
mapGetters?
然后再配上展开符“...”
这样是方便组合?
挂载到组件
el-menu :default-active="loraMemu"
在 computed:{}设置好。
loraMemu(){
return this.$store.state.loraMemu
}
这样状态多了,每个写过去比较繁琐。
mapState(['loraMemu'])
这样写起来简便点??
多个状态--形成模块化
modules: {
storeA:{}
storeB:{}
}
然后形成一个根模块(root)和模块之间的调用关系
模块化后还是共用一个命名列表,里面的action这些不能起相同名字。
加入了命名空间概念
模块中设置
namespaced: true
获取
this.$store.state.moduleA.countA 就是加了模块名。
模块名又嫌太长了。
import { createNamespacedHelpers } from 'vuex'
const { mapState, mapActions } = createNamespacedHelpers('some/nested/module')
这样就等于已经进入该模块路径,不用再加模块名。
lamp系统中
用在页面里的 <el-menu :default-active="nbMemu">
维护menu的选择状态。不知为啥要用这种机制。
有多个状态模块
modules: { jsSIP, menu },
有命名空间
namespaced: true,
页面交互
store\index.js
state:{item{}}
menu.vue
computed: {
item: <-> this.$store.state.item}
el-select v-model="item"
组件/component
一般是两个部分
- html的模板 <template>
这里的html是在vue渲染下的,是vue特定的模板语法,
要有个根节点
不能并列有多个 - 脚本 <script>
这里的语法应该是es6规范的。
只是vue提供了框架模块,来组织整个应用。
这里组件嵌套的时候,要import引入,要在components中再次申明使用的组件名。