彻底搞懂并实现webpack热更新原理
彻底搞懂并实现webpack热更新原理
目录
HMR是什么
配置使用HMR
配置webpack
解析webpack打包后的文件内容
配置HMR
HMR原理
debug服务端源码
debug客户端源码
问题
总结
HMR是什么
HMR
即Hot Module Replacement
是指当你对代码修改并保存后,webpack
将会对代码进行重新打包,并将改动的模块发送到浏览器端,浏览器用新的模块替换掉旧的模块,去实现局部更新页面而非整体刷新页面。接下来将从使用到实现一版简易功能带领大家深入浅出HMR
。
文章首发于@careteen/webpack-hmr,转载请注明来源即可。
使用场景
如上图所示,一个注册页面包含用户名
、密码
、邮箱
三个必填输入框,以及一个提交
按钮,当你在调试邮箱
模块改动了代码时,没做任何处理情况下是会刷新整个页面,频繁的改动代码会浪费你大量时间去重新填写内容。预期是保留用户名
、密码
的输入内容,而只替换邮箱
这一模块。这一诉求就需要借助webpack-dev-server
的热模块更新功能。
相对于live reload
整体刷新页面的方案,HMR
的优点在于可以保存应用的状态,提高开发效率。
配置使用HMR
配置webpack
首先借助webpack
搭建项目
mkdir webpack-hmr && cd webpack-hmr
npm i -y
npm i -S webpack webpack-cli webpack-dev-server html-webpack-plugin
const path = require('path')
const webpack = require('webpack')
const htmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode: 'development', // 开发模式不压缩代码,方便调试
entry: './src/index.js', // 入口文件
output: {
path: path.join(__dirname, 'dist'),
filename: 'main.js'
},
devServer: {
contentBase: path.join(__dirname, 'dist')
},
plugins: [
new htmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html'
})
]
}
Webpack Hot Module Replacement
var root = document.getElementById('root')
function render () {
root.innerHTML = require('./content.js')
}
render()
新建依赖文件src/content.js
导出字符供index渲染页面
var ret = 'Hello Webpack Hot Module Replacement'
module.exports = ret
// export default ret
"scripts": {
"dev": "webpack-dev-server",
"build": "webpack"
}
然后npm run dev
即可启动项目
通过npm run build
打包生成静态资源到dist
目录
接下来先分析下dist
目录中的文件
解析webpack打包后的文件内容
webpack自己实现的一套commonjs规范讲解
区分commonjs和esmodule
dist目录结构
.
├── index.html
└── main.js
其中index.html
内容如下
使用html-webpack-plugin
插件将入口文件及其依赖通过script
标签引入
先对main.js
内容去掉注释和无关内容进行分析
(function (modules) { // webpackBootstrap
// ...
})
({
"./src/content.js":
(function (module, exports) {
eval("var ret = 'Hello Webpack Hot Module Replacement'\n\nmodule.exports = ret\n// export default ret\n\n");
}),
"./src/index.js": (function (module, exports, __webpack_require__) {
eval("var root = document.getElementById('root')\nfunction render () {\n root.innerHTML = __webpack_require__(/*! ./content.js */ \"./src/content.js\")\n}\nrender()\n\n\n");
})
});
可见webpack打包后会产出一个自执行函数,其参数为一个对象
"./src/content.js": (function (module, exports) {
eval("...")
}
键为入口文件或依赖文件相对于根目录的相对路径,值则是一个函数,其中使用eval
执行文件的内容字符。
再进入自执行函数体内,可见webpack自己实现了一套commonjs
规范
(function (modules) {
// 模块缓存
var installedModules = {};
function __webpack_require__(moduleId) {
// 判断是否有缓存
if (installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
// 没有缓存则创建一个模块对象并将其放入缓存
var module = installedModules[moduleId] = {
i: moduleId,
l: false, // 是否已加载
exports: {}
};
// 执行模块函数
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
// 将状态置为已加载
module.l = true;
// 返回模块对象
return module.exports;
}
// ...
// 加载入口文件
return __webpack_require__(__webpack_require__.s = "./src/index.js");
})
如果对上面
commonjs
规范感兴趣可以前往我的另一篇文章 手摸手带你实现commonjs规范
给出上面代码主要是先对webpack的产出文件混个眼熟,不要惧怕。其实任何一个不管多复杂的事物都是由更小更简单的东西组成,剖开它认识它爱上它。
配置HMR
接下来配置并感受一下热更新带来的便捷开发
webpack.config.js
配置
// ...
devServer: {
hot: true
}
// ...
./src/index.js
配置
// ...
if (module.hot) {
module.hot.accept(['./content.js'], () => {
render()
})
}
当更改./content.js
的内容并保存时,可以看到页面没有刷新,但是内容已经被替换了。
这对提高开发效率意义重大。接下来将一层层剖开它,认识它的实现原理。
HMR原理
如上图所示,右侧Server
端使用webpack-dev-server
去启动本地服务,内部实现主要使用了webpack
、express
、websocket
。
上图先只看个大概,下面将从服务端和客户端两个方面进行详细分析
debug服务端源码
现在也只需要关注上图的右侧服务端部分,左侧可以暂时忽略。下面步骤主要是debug服务端源码分析其详细思路,也给出了代码所处的具体位置,感兴趣的可以先行定位到下面的代码处设置断点,然后观察数据的变化情况。也可以先跳过阅读此步骤。
启动webpack-dev-server
服务器,源代码地址@webpack-dev-server/webpack-dev-server.js#L173
创建webpack实例,源代码地址@webpack-dev-server/webpack-dev-server.js#L89
创建Server服务器,源代码地址@webpack-dev-server/webpack-dev-server.js#L107
添加webpack的done事件回调,源代码地址@webpack-dev-server/Server.js#L122
编译完成向客户端发送消息,源代码地址@webpack-dev-server/Server.js#L184
创建express应用app,源代码地址@webpack-dev-server/Server.js#L123
设置文件系统为内存文件系统,源代码地址@webpack-dev-middleware/fs.js#L115
添加webpack-dev-middleware中间件,源代码地址@webpack-dev-server/Server.js#L125
中间件负责返回生成的文件,源代码地址@webpack-dev-middleware/middleware.js#L20
启动webpack编译,源代码地址@webpack-dev-middleware/index.js#L51
创建http服务器并启动服务,源代码地址@webpack-dev-server/Server.js#L135
使用sockjs在浏览器端和服务端之间建立一个 websocket 长连接,源代码地址@webpack-dev-server/Server.js#L745
创建socket服务器,源代码地址@webpack-dev-server/SockJSServer.js#L34
服务端简易实现
上面是我通过debug得出dev-server运行流程比较核心的几个点,下面将其抽象整合到一个文件中。
启动webpack-dev-server服务器
先导入所有依赖
const path = require('path') // 解析文件路径
const express = require('express') // 启动本地服务
const mime = require('mime') // 获取文件类型 实现一个静态服务器
const webpack = require('webpack') // 读取配置文件进行打包
const MemoryFileSystem = require('memory-fs') // 使用内存文件系统更快,文件生成在内存中而非真实文件
const config = require('./webpack.config') // 获取webpack配置文件
创建webpack实例
const compiler = webpack(config)
compiler代表整个webpack编译任务,全局只有一个
创建Server服务器
class Server {
constructor(compiler) {
this.compiler = compiler
}
listen(port) {
this.server.listen(port, () => {
console.log(`服务器已经在${port}端口上启动了`)
})
}
}
let server = new Server(compiler)
server.listen(8000)
在后面是通过express来当启动服务的
添加webpack的done事件回调
constructor(compiler) {
let sockets = []
let lasthash
compiler.hooks.done.tap('webpack-dev-server', (stats) => {
lasthash = stats.hash
// 每当新一个编译完成后都会向客户端发送消息
sockets.forEach(socket => {
socket.emit('hash', stats.hash) // 先向客户端发送最新的hash值
socket.emit('ok') // 再向客户端发送一个ok
})
})
}
webpack
编译后提供提供了一系列钩子函数,以供插件能访问到它的各个生命周期节点,并对其打包内容做修改。compiler.hooks.done
则是插件能修改其内容的最后一个节点。
编译完成通过socket
向客户端发送消息,推送每次编译产生的hash
。另外如果是热更新的话,还会产出二个补丁文件,里面描述了从上一次结果到这一次结果都有哪些chunk和模块发生了变化。
使用let sockets = []
数组去存放当打开了多个Tab时每个Tab的socket实例
。
创建express应用app
let app = new express()
设置文件系统为内存文件系统
let fs = new MemoryFileSystem()
使用MemoryFileSystem
将compiler
的产出文件打包到内存中。
添加webpack-dev-middleware中间件
function middleware(req, res, next) {
if (req.url === '/favicon.ico') {
return res.sendStatus(404)
}
// /index.html dist/index.html
let filename = path.join(config.output.path, req.url.slice(1))
let stat = fs.statSync(filename)
if (stat.isFile()) { // 判断是否存在这个文件,如果在的话直接把这个读出来发给浏览器
let content = fs.readFileSync(filename)
let contentType = mime.getType(filename)
res.setHeader('Content-Type', contentType)
res.statusCode = res.statusCode || 200
res.send(content)
} else {
return res.sendStatus(404)
}
}
app.use(middleware)
使用expres启动了本地开发服务后,使用中间件去为其构造一个静态服务器,并使用了内存文件系统,使读取文件后存放到内存中,提高读写效率,最终返回生成的文件。
启动webpack编译
compiler.watch({}, err => {
console.log('又一次编译任务成功完成了')
})
以监控的模式启动一次webpack编译,当编译成功之后执行回调
创建http服务器并启动服务
constructor(compiler) {
// ...
this.server = require('http').createServer(app)
// ...
}
listen(port) {
this.server.listen(port, () => {
console.log(`服务器已经在${port}端口上启动了`)
})
}
使用sockjs在浏览器端和服务端之间建立一个 websocket 长连接
constructor(compiler) {
// ...
this.server = require('http').createServer(app)
let io = require('socket.io')(this.server)
io.on('connection', (socket) => {
sockets.push(socket)
socket.emit('hash', lastHash)
socket.emit('ok')
})
}
启动一个 websocket服务器,然后等待连接来到,连接到来之后存进sockets池
当有文件改动,webpack重新编译时,向客户端推送hash
和ok
两个事件
服务端调试阶段
感兴趣的可以根据上面debug服务端源码所带的源码位置,并在浏览器的调试模式下设置断点查看每个阶段的值。
node dev-server.js
使用我们自己编译的dev-server.js
启动服务,可看到页面可以正常展示,但还没有实现热更新。
下面将调式客户端的源代码分析其实现流程。
debug客户端源码
现在也只需要关注上图的左侧客户端部分,右侧可以暂时忽略。下面步骤主要是debug客户端源码分析其详细思路,也给出了代码所处的具体位置,感兴趣的可以先行定位到下面的代码处设置断点,然后观察数据的变化情况。也可以先跳过阅读此步骤。
debug客户端源码分析其详细思路
webpack-dev-server/client端会监听到此hash消息,源代码地址@webpack-dev-server/index.js#L54
客户端收到ok的消息后会执行reloadApp方法进行更新,源代码地址index.js#L101
在reloadApp中会进行判断,是否支持热更新,如果支持的话发射webpackHotUpdate事件,如果不支持则直接刷新浏览器,源代码地址reloadApp.js#L7
在webpack/hot/dev-server.js会监听webpackHotUpdate事件,源代码地址dev-server.js#L55
在check方法里会调用module.hot.check方法,源代码地址dev-server.js#L13
HotModuleReplacement.runtime请求Manifest,源代码地址HotModuleReplacement.runtime.js#L180
它通过调用 JsonpMainTemplate.runtime的hotDownloadManifest方法,源代码地址JsonpMainTemplate.runtime.js#L23
调用JsonpMainTemplate.runtime的hotDownloadUpdateChunk方法通过JSONP请求获取到最新的模块代码,源代码地址JsonpMainTemplate.runtime.js#L14
补丁JS取回来后会调用JsonpMainTemplate.runtime.js的webpackHotUpdate方法,源代码地址JsonpMainTemplate.runtime.js#L8
然后会调用HotModuleReplacement.runtime.js的hotAddUpdateChunk方法动态更新模块代码,源代码地址HotModuleReplacement.runtime.js#L222
然后调用hotApply方法进行热更新,源代码地址HotModuleReplacement.runtime.js#L257、HotModuleReplacement.runtime.js#L278
客户端简易实现
上面是我通过debug得出dev-server运行流程比较核心的几个点,下面将其抽象整合成一个文件。
webpack-dev-server/client端会监听到此hash消息
在开发客户端功能之前,需要在src/index.html
中引入socket.io
下面连接socket并接受消息
let socket = io('/')
socket.on('connect', onConnected)
const onConnected = () => {
console.log('客户端连接成功')
}
let hotCurrentHash // lastHash 上一次 hash值
let currentHash // 这一次的hash值
socket.on('hash', (hash) => {
currentHash = hash
})
将服务端webpack每次编译所产生hash
进行缓存
客户端收到ok的消息后会执行reloadApp方法进行更新
socket.on('ok', () => {
reloadApp(true)
})
reloadApp中判断是否支持热更新
// 当收到ok事件后,会重新刷新app
function reloadApp(hot) {
if (hot) { // 如果hot为true 走热更新的逻辑
hotEmitter.emit('webpackHotUpdate')
} else { // 如果不支持热更新,则直接重新加载
window.location.reload()
}
}
在reloadApp中会进行判断,是否支持热更新,如果支持的话发射webpackHotUpdate事件,如果不支持则直接刷新浏览器。
在webpack/hot/dev-server.js会监听webpackHotUpdate事件
首先需要一个发布订阅去绑定事件并在合适的时机触发。
class Emitter {
constructor() {
this.listeners = {}
}
on(type, listener) {
this.listeners[type] = listener
}
emit(type) {
this.listeners[type] && this.listeners[type]()
}
}
let hotEmitter = new Emitter()
hotEmitter.on('webpackHotUpdate', () => {
if (!hotCurrentHash || hotCurrentHash == currentHash) {
return hotCurrentHash = currentHash
}
hotCheck()
})
会判断是否为第一次进入页面和代码是否有更新。
上面的发布订阅较为简单,且只支持先发布后订阅功能。对于一些较为复杂的场景可能需要先订阅后发布,此时可以移步 @careteen/event-emitter。其实现原理也挺简单,需要维护一个离线事件栈存放还没发布就订阅的事件,等到订阅时可以取出所有事件执行。
在check方法里会调用module.hot.check方法
function hotCheck() {
hotDownloadManifest().then(update => {
let chunkIds = Object.keys(update.c)
chunkIds.forEach(chunkId => {
hotDownloadUpdateChunk(chunkId)
})
})
}
上面也提到过webpack每次编译都会产生hash值
、已改动模块的json文件
、已改动模块代码的js文件
,
此时先使用ajax
请求Manifest
即服务器这一次编译相对于上一次编译改变了哪些module和chunk。
然后再通过jsonp
获取这些已改动的module和chunk的代码。
调用hotDownloadManifest方法
function hotDownloadManifest() {
return new Promise(function (resolve) {
let request = new XMLHttpRequest()
//hot-update.json文件里存放着从上一次编译到这一次编译 取到差异
let requestPath = '/' + hotCurrentHash + ".hot-update.json"
request.open('GET', requestPath, true)
request.onreadystatechange = function () {
if (request.readyState === 4) {
let update = JSON.parse(request.responseText)
resolve(update)
}
}
request.send()
})
}
调用hotDownloadUpdateChunk方法通过JSONP请求获取到最新的模块代码
function hotDownloadUpdateChunk(chunkId) {
let script = document.createElement('script')
script.charset = 'utf-8'
// /main.xxxx.hot-update.js
script.src = '/' + chunkId + "." + hotCurrentHash + ".hot-update.js"
document.head.appendChild(script)
}
这里解释下为什么使用JSONP
获取而不直接利用socket
获取最新代码?主要是因为JSONP
获取的代码可以直接执行。
调用webpackHotUpdate方法
当客户端把最新的代码拉到浏览之后
window.webpackHotUpdate = function (chunkId, moreModules) {
// 循环新拉来的模块
for (let moduleId in moreModules) {
// 从模块缓存中取到老的模块定义
let oldModule = __webpack_require__.c[moduleId]
// parents哪些模块引用这个模块 children这个模块引用了哪些模块
// parents=['./src/index.js']
let {
parents,
children
} = oldModule
// 更新缓存为最新代码 缓存进行更新
let module = __webpack_require__.c[moduleId] = {
i: moduleId,
l: false,
exports: {},
parents,
children,
hot: window.hotCreateModule(moduleId)
}
moreModules[moduleId].call(module.exports, module, module.exports, __webpack_require__)
module.l = true // 状态变为加载就是给module.exports 赋值了
parents.forEach(parent => {
// parents=['./src/index.js']
let parentModule = __webpack_require__.c[parent]
// _acceptedDependencies={'./src/title.js',render}
parentModule && parentModule.hot && parentModule.hot._acceptedDependencies[moduleId] && parentModule.hot._acceptedDependencies[moduleId]()
})
hotCurrentHash = currentHash
}
}
hotCreateModule的实现
实现我们可以在业务代码中定义需要热更新的模块以及回调函数,将其存放在hot._acceptedDependencies
中。
window.hotCreateModule = function () {
let hot = {
_acceptedDependencies: {},
dispose() {
// 销毁老的元素
},
accept: function (deps, callback) {
for (let i = 0; i < deps.length; i++) {
// hot._acceptedDependencies={'./title': render}
hot._acceptedDependencies[deps[i]] = callback
}
}
}
return hot
}
然后在webpackHotUpdate
中进行调用
parents.forEach(parent => {
// parents=['./src/index.js']
let parentModule = __webpack_require__.c[parent]
// _acceptedDependencies={'./src/title.js',render}
parentModule && parentModule.hot && parentModule.hot._acceptedDependencies[moduleId] && parentModule.hot._acceptedDependencies[moduleId]()
})
最后调用hotApply方法进行热更新
客户端调试阶段
经过上述实现了一个基本版的HMR,可更改代码保存的同时查看浏览器并非整体刷新,而是局部更新代码进而更新视图。在涉及到大量表单的需求时大大提高了开发效率。
问题
感兴趣的可前往 debug CommonJs规范了解其实现原理。
webpack实现流程以及各个生命周期的作用是什么?
webpack主要借助了
tapable
这个库所提供的一系列同步/异步钩子函数贯穿整个生命周期。
基于此我实现了一版简易的 webpack,源码100+行,食用时伴着注释很容易消化,感兴趣的可前往看个思路。
发布订阅的使用和实现,并且如何实现一个可先订阅后发布的机制?
上面也提到需要使用到发布订阅模式,且只支持先发布后订阅功能。对于一些较为复杂的场景可能需要先订阅后发布,此时可以移步 @careteen/event-emitter。其实现原理也挺简单,需要维护一个离线事件栈存放还没发布就订阅的事件,等到订阅时可以取出所有事件执行。
为什么使用JSONP而不用socke通信获取更新过的代码?
因为通过socket通信获取的是一串字符串需要再做处理。而通过
JSONP
获取的代码可以直接执行。
引用
你可能感兴趣的:(【Webpack点滴知识,】)
学点心理知识,呵护孩子健康
静候花开_7090
昨天听了华中师范大学教育管理学系副教授张玲老师的《哪里才是学生心理健康的最后庇护所,超越教育与技术的思考》的讲座。今天又重新学习了一遍,收获匪浅。张玲博士也注意到了当今社会上的孩子由于心理问题导致的自残、自杀及伤害他人等恶性事件。她向我们普及了一个重要的命题,她说心理健康的一些基本命题,我们与我们通常的一些教育命题是不同的,她还举了几个例子,让我们明白我们原来以为的健康并非心理学上的健康。比如如果
2021年12月19日,春蕾教育集团团建活动感受——黄晓丹
黄错错加油
感受:1.从陌生到熟悉的过程。游戏环节让我们在轻松的氛围中得到了锻炼,也增长了不少知识。2.游戏过程中,我们贡献的是个人力量,展现的是团队的力量。它磨合的往往不止是工作的熟悉,更是观念上契合度的贴近。3.这和工作是一样的道理。在各自的岗位上,每个人摆正自己的位置、各司其职充分发挥才能,并团结一致劲往一处使,才能实现最大的成功。新知:1.团队精神需要不断地创新。过去,人们把创新看作是冒风险,现在人们
高端密码学院笔记285
柚子_b4b4
高端幸福密码学院(高级班)幸福使者:李华第(598)期《幸福》之回归内在深层生命原动力基础篇——揭秘“激励”成长的喜悦心理案例分析主讲:刘莉一,知识扩充:成功=艰苦劳动+正确方法+少说空话。贪图省力的船夫,目标永远下游。智者的梦再美,也不如愚人实干的脚印。幸福早课堂2020.10.16星期五一笔记:1,重视和珍惜的前提是知道它的价值非常重要,当你珍惜了,你就真正定下来,真正的学到身上。2,大家需要
从0到500+,我是如何利用自媒体赚钱?
一列脚印
运营公众号半个多月,从零基础的小白到现在慢慢懂了一些运营的知识。做好公众号是很不容易的,要做很多事情;排版、码字、引流…通通需要自己解决,业余时间全都花费在这上面涨这么多粉丝是真的不容易,对比知乎大佬来说,我们这种没资源,没人脉,还没钱的小透明来说,想要一个月涨粉上万,怕是今天没睡醒(不过你有的方法,算我piapia打脸)至少我是清醒的,自己慢慢努力,实现我的万粉目标!大家快来围观、支持我吧!孩子
利用LangChain的StackExchange组件实现智能问答系统
nseejrukjhad
langchain microsoft 数据库 python
利用LangChain的StackExchange组件实现智能问答系统引言在当今的软件开发世界中,StackOverflow已经成为程序员解决问题的首选平台之一。而LangChain作为一个强大的AI应用开发框架,提供了StackExchange组件,使我们能够轻松地将StackOverflow的海量知识库集成到我们的应用中。本文将详细介绍如何使用LangChain的StackExchange组件
关于城市旅游的HTML网页设计——(旅游风景云南 5页)HTML+CSS+JavaScript
二挡起步
web前端期末大作业 javascript html css 旅游 风景
⛵源码获取文末联系✈Web前端开发技术描述网页设计题材,DIV+CSS布局制作,HTML+CSS网页设计期末课程大作业|游景点介绍|旅游风景区|家乡介绍|等网站的设计与制作|HTML期末大学生网页设计作业,Web大学生网页HTML:结构CSS:样式在操作方面上运用了html5和css3,采用了div+css结构、表单、超链接、浮动、绝对定位、相对定位、字体样式、引用视频等基础知识JavaScrip
HTML网页设计制作大作业(div+css) 云南我的家乡旅游景点 带文字滚动
二挡起步
web前端期末大作业 web设计网页规划与设计 html css javascript dreamweaver 前端
Web前端开发技术描述网页设计题材,DIV+CSS布局制作,HTML+CSS网页设计期末课程大作业游景点介绍|旅游风景区|家乡介绍|等网站的设计与制作HTML期末大学生网页设计作业HTML:结构CSS:样式在操作方面上运用了html5和css3,采用了div+css结构、表单、超链接、浮动、绝对定位、相对定位、字体样式、引用视频等基础知识JavaScript:做与用户的交互行为文章目录前端学习路线
Day1笔记-Python简介&标识符和关键字&输入输出
~在杰难逃~
Python python 开发语言 大数据 数据分析 数据挖掘
大家好,从今天开始呢,杰哥开展一个新的专栏,当然,数据分析部分也会不定时更新的,这个新的专栏主要是讲解一些Python的基础语法和知识,帮助0基础的小伙伴入门和学习Python,感兴趣的小伙伴可以开始认真学习啦!一、Python简介【了解】1.计算机工作原理编程语言就是用来定义计算机程序的形式语言。我们通过编程语言来编写程序代码,再通过语言处理程序执行向计算机发送指令,让计算机完成对应的工作,编程
webpack图片等资源的处理
dmengmeng
需要的loaderfile-loader(让我们可以引入这些资源文件)url-loader(其实是file-loader的二次封装)img-loader(处理图片所需要的)在没有使用任何处理图片的loader之前,比如说css中用到了背景图片,那么最后打包会报错的,因为他没办法处理图片。其实你只想能够使用图片的话。只加一个file-loader就可以,打开网页能准确看到图片。{test:/\.(p
走向以教育叙事为载体的教育叙事研究
666小飞鱼
今天我读了吴松超老师的《给教师的68条建写作建议》中的第23条《如何通过教育叙事走向研究》,吴老师在文中与我们分享了一个德育案例,这是一个反面的案例,意在告知我们在处理问题时,不能就考虑的点太窄,思考要全面。走向教育叙事研究,教师要有敏锐的“感知力”,这个感知力来自于背后专业知识的支撑,思维能力以及广阔的视野和见识等。所以对于同一件事处理方法不同,这个就是教师背后“敏锐力”的不同造成的,也就是说是
数据仓库——维度表一致性
墨染丶eye
背诵 数据仓库
数据仓库基础笔记思维导图已经整理完毕,完整连接为:数据仓库基础知识笔记思维导图维度一致性问题从逻辑层面来看,当一系列星型模型共享一组公共维度时,所涉及的维度称为一致性维度。当维度表存在不一致时,短期的成功难以弥补长期的错误。维度时确保不同过程中信息集成起来实现横向钻取货活动的关键。造成横向钻取失败的原因维度结构的差别,因为维度的差别,分析工作涉及的领域从简单到复杂,但是都是通过复杂的报表来弥补设计
自我意识
徐立华
----读帕克.帕尔默《教学勇气》(P18----19)5.铸造我们的学科帕克.帕尔默说学科知识对我们的自身认同和外部世界有启发意义。学科会铸造我们。“在我们与学科的命题概念和学科的生活框架相遇之前,自我意识知识处于潜伏状态,通过回想学科是怎样唤醒自我意识,我们就可以找回教学心灵。”《教学勇气》(P18)我们的自我意识像冰山表面下无限延伸的冰层,常常处于潜伏状态。但是在我们对所教授的学科进行深入思
ARM驱动学习之基础小知识
JT灬新一
ARM 嵌入式 arm开发 学习
ARM驱动学习之基础小知识•sch原理图工程师工作内容–方案–元器件选型–采购(能不能买到,价格)–原理图(涉及到稳定性)•layout画板工程师–layout(封装、布局,布线,log)(涉及到稳定性)–焊接的一部分工作(调试阶段板子的焊接)•驱动工程师–驱动,原理图,layout三部分的交集容易发生矛盾•PCB研发流程介绍–方案,原理图(网表)–layout工程师(gerber文件)–PCB板
ARM驱动学习之5 LEDS驱动
JT灬新一
嵌入式 C 底层 arm开发 学习 单片机
ARM驱动学习之5LEDS驱动知识点:•linuxGPIO申请函数和赋值函数–gpio_request–gpio_set_value•三星平台配置GPIO函数–s3c_gpio_cfgpin•GPIO配置输出模式的宏变量–S3C_GPIO_OUTPUT注意点:DRIVER_NAME和DEVICE_NAME匹配。实现步骤:1.加入需要的头文件://Linux平台的gpio头文件#include//三
Rust基础知识
GRKF15
rust 开发语言 后端
1.Rust语言简介1.1基础语法变量声明:let关键字用于声明变量,可以指定或不指定类型,如leta=10;和letmutc=30i32;。函数定义:使用fn关键字定义函数,并指定参数类型及返回类型,如fnadd(i:i32,j:i32)->i32{i+j}。控制流:包括if、else等,控制语句后需要使用;来结束语句。1.2数据类型整数类型:i8、i16、i32、i64、i128,以及无符号的
C++菜鸟教程 - 从入门到精通 第二节
DreamByte
c++
一.上节课的补充(数据类型)1.前言继上节课,我们主要讲解了输入,输出和运算符,我们现在来补充一下数据类型的知识上节课遗漏了这个知识点,非常的抱歉顺便说一下,博主要上高中了,更新会慢,2-4周更新一次对了,正好赶上中秋节,小编跟大家说一句:中秋节快乐!2.int类型上节课,我们其实只用了int类型int类型,是整数类型,它们存贮的是整数,不能存小数(浮点数)定义变量的方式很简单inta;//定义一
【华为OD技术面试真题精选 - 非技术题】 -HR面,综合面_华为od hr面
一个射手座的程序媛
程序员 华为od 面试 职场和发展
最后的话最近很多小伙伴找我要Linux学习资料,于是我翻箱倒柜,整理了一些优质资源,涵盖视频、电子书、PPT等共享给大家!资料预览给大家整理的视频资料:给大家整理的电子书资料:如果本文对你有帮助,欢迎点赞、收藏、转发给朋友,让我有持续创作的动力!网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。需要这份系统化的资料的朋友,可以点击这里获
学习“论语”-第59天
春峰轩
12.14子张问政。子曰:“居之无倦,行之以忠。”子张问为政之道。孔子说:“在位尽职不懈怠,执行政令要忠诚。”12.15子曰:“博学于文,约之以礼,亦可以弗畔矣夫!”孔子说:“君子广泛地学习文献,并且用礼节约束自己,也就不会离经叛道了。”12.16子曰:“君子成人之美,不成人之恶。小人反是。”孔子说:“君子成全别人的好事,而不助长别人的坏处。小人则与此相反行事。”知识点:“成人之美,不成人之恶”贯
nosql数据库技术与应用知识点
皆过客,揽星河
NoSQL nosql 数据库 大数据 数据分析 数据结构 非关系型数据库
Nosql知识回顾大数据处理流程数据采集(flume、爬虫、传感器)数据存储(本门课程NoSQL所处的阶段)Hdfs、MongoDB、HBase等数据清洗(入仓)Hive等数据处理、分析(Spark、Flink等)数据可视化数据挖掘、机器学习应用(Python、SparkMLlib等)大数据时代存储的挑战(三高)高并发(同一时间很多人访问)高扩展(要求随时根据需求扩展存储)高效率(要求读写速度快)
广州会刊小程序开发公司哪家好|开发多少钱费用|专业外包服务
红匣子实力推荐
在选择广州会刊小程序开发公司时,有几个关键因素需要考虑。首先,您应该确定自己的需求和目标,以便找到最合适的开发公司。其次,您需要考虑公司的经验和专业知识。最后,您还应该考虑公司的信誉和口碑。开发-联系电话:13642679953(微信同号)首先,您应该明确自己的需求和目标。会刊小程序是一种用于展示会议信息和日程安排的应用程序。在选择开发公司之前,您应该明确自己的需求,包括功能要求、设计风格和用户体
读《人间鲁迅》有感
琳语读书
上周读完《闻一多传》后,我对中国近代知识分子产生了兴趣,这周继续读了《人间鲁迅》。厚厚的两本书,记录了一个人的一生,苦痛,彷徨和挣扎,虽然只读了一小部分,却也心潮澎湃。闻一多和鲁迅是完全不同的。鲁迅是沉郁的,现实的,寂寞的,抗争的。除了天生性格的不同外,环境的塑造也是非常之大。鲁迅少年经历了家庭的变故,看尽了人间冷暖,世态炎凉。这种经历促使他很早就观察思考人生,立志用文学来改变中国国民的劣根。闻一
OPENAIGC开发者大赛企业组AI黑马奖 | AIGC数智传媒解决方案
RPA中国
人工智能 AIGC 传媒
在第二届拯救者杯OPENAIGC开发者大赛中,涌现出一批技术突出、创意卓越的作品。为了让这些优秀项目被更多人看到,我们特意开设了优秀作品报道专栏,旨在展示其独特之处和开发者的精彩故事。无论您是技术专家还是爱好者,希望能带给您不一样的知识和启发。让我们一起探索AIGC的无限可能,见证科技与创意的完美融合!创未来AI应用赛-企业组AI黑马奖作品名称:AIGC数智传媒解决方案参赛团队:深圳市三象智能技术
2022-08-28
蔚蓝一片晴
初三暑假培训收获点滴从8月25至8月27日三天两晚的培训结束了,回到家中,该静下心来整理一下触动心灵的收获,成为成长的积淀。1.在优秀团队中快速成长与提升,做一名反思成长型教师一名专业型教师的教学指导包括了教学原理知识、案例知识、策略知识。面对教学中的遇到的有趣的情形、问题会去研究其理,寻找更好的教法学法对策。从新手到成熟型教师,再走向专业型教师,需要的是觉醒与反思,多进行案例研究,从案例中观察、
2021-07-31
比峰
七月的最后一天,过了今天,就是八月,心脏在颤抖……昨天两点半才睡,一直在以两倍的语速的听之前的课程,虽然隔得时间不长,但是很多知识点已经忘了差不多了,为了让自己能够掌握的稍微全面一点,还是磨刀不误砍柴工的比较好。正因为晚上睡得晚,今天一上午的状态都不好,也可能因为上午都是待在家里,所以多数时间自己是在补觉。既然太累,那就睡觉吧,总比浪费时间的好。下午到咖啡馆做题,一道差错更正一下子让自己的实力暴露
你可能遗漏的一些C#/.NET/.NET Core知识点
追逐时光者
C# .NET DotNetGuide编程指南 c# .net .netcore microsoft
前言在这个快速发展的技术世界中,时常会有一些重要的知识点、信息或细节被忽略或遗漏。《C#/.NET/.NETCore拾遗补漏》专栏我们将探讨一些可能被忽略或遗漏的重要知识点、信息或细节,以帮助大家更全面地了解这些技术栈的特性和发展方向。拾遗补漏GitHub开源地址https://github.com/YSGStudyHards/DotNetGuide/blob/main/docs/DotNet/D
2023-08-08
2023梦启支教团张牧泽
学汉字历史,行传统书法——中国矿业大学梦启支教团梦启三班开展书法文化课7月20日上午8时,中国矿业大学梦启支教团在贵州省金沙县西洛街道彩虹小学开展了“书法文化”课程。该课程意在向孩子们传授汉字演变的相关知识,围绕书法发展历史讲解不同时期的字形字体特点。此课程由梦启支教团成员王耀民讲授,梦启三班全体成员参加。中国文字的发展有数千年的历史,从早期雏形的象形文字到殷商时期的甲骨文、金文,再到西周、秦朝的
MongoDB知识概括
GeorgeLin98
持久层 mongodb
MongoDB知识概括MongoDB相关概念单机部署基本常用命令索引-IndexSpirngDataMongoDB集成副本集分片集群安全认证MongoDB相关概念业务应用场景:传统的关系型数据库(如MySQL),在数据操作的“三高”需求以及应对Web2.0的网站需求面前,显得力不从心。解释:“三高”需求:①Highperformance-对数据库高并发读写的需求。②HugeStorage-对海量数
mac 备份android 手机通讯录导入iphone,iphone如何导出通讯录(轻松教你iPhone备份通讯录的方法)...
weixin_39762838
mac 备份android 手机通讯录导入iphone
在日新月异的手机更替中,换手机已经成为一个非常稀松平常的事情,但将旧手机上面的通讯录导入到新手机还是让不少小伙伴为难,本篇将给大家详细讲解这方面的知识:“苹果手机通讯录怎么导入到新手机”及“安卓手机通讯录导入到新手机”的方法。一、苹果手机通讯录导入到新手机常用方法(SIM卡导入)在苹果手机主频幕上找到“设置”,单击进入设置菜单,下拉菜单列表,点击“邮件、通讯录、日历”,然后找到“导入SIM卡通讯录
android 更改窗口的层次,浮窗开发之窗口层级
Ms.Bu
android 更改窗口的层次
最近在项目中遇到了这样的需求:需要在特定的其他应用之上悬浮自己的UI交互(拖动、输入等复杂的UI交互),和九游的浮窗类似,不过我们的比九游的体验更好,我们越过了很多授权的限制。浮窗效果很多人都知道如何去实现一个简单的浮窗,但是却很少有人去深入的研究背后的流程机制,由于项目中浮窗交互比较复杂,遇到了些坑查看了很多资料,故总结浮窗涉及到的知识点:窗口层级关系(浮窗是如何“浮”的)?浮窗有哪些限制,如何
Python入门之Lesson2:Python基础语法
小熊同学哦
Python入门课程 python 开发语言 算法 数据结构 青少年编程
目录前言一.介绍1.变量和数据类型2.常见运算符3.输入输出4.条件语句5.循环结构二.练习三.总结前言欢迎来到《Python入门》系列博客的第二课。在上一课中,我们了解了Python的安装及运行环境的配置。在这一课中,我们将深入学习Python的基础语法,这是编写Python代码的根基。通过本节内容的学习,你将掌握变量、数据类型、运算符、输入输出、条件语句等Python编程的基础知识。一.介绍1
开发者关心的那些事
圣子足道
ios 游戏 编程 apple 支付
我要在app里添加IAP,必须要注册自己的产品标识符(product identifiers)。产品标识符是什么?
产品标识符(Product Identifiers)是一串字符串,它用来识别你在应用内贩卖的每件商品。App Store用产品标识符来检索产品信息,标识符只能包含大小写字母(A-Z)、数字(0-9)、下划线(-)、以及圆点(.)。你可以任意排列这些元素,但我们建议你创建标识符时使用
负载均衡器技术Nginx和F5的优缺点对比
bijian1013
nginx F5
对于数据流量过大的网络中,往往单一设备无法承担,需要多台设备进行数据分流,而负载均衡器就是用来将数据分流到多台设备的一个转发器。
目前有许多不同的负载均衡技术用以满足不同的应用需求,如软/硬件负载均衡、本地/全局负载均衡、更高
LeetCode[Math] - #9 Palindrome Number
Cwind
java Algorithm 题解 LeetCode Math
原题链接:#9 Palindrome Number
要求:
判断一个整数是否是回文数,不要使用额外的存储空间
难度:简单
分析:
题目限制不允许使用额外的存储空间应指不允许使用O(n)的内存空间,O(1)的内存用于存储中间结果是可以接受的。于是考虑将该整型数反转,然后与原数字进行比较。
注:没有看到有关负数是否可以是回文数的明确结论,例如
画图板的基本实现
15700786134
画图板
要实现画图板的基本功能,除了在qq登陆界面中用到的组件和方法外,还需要添加鼠标监听器,和接口实现。
首先,需要显示一个JFrame界面:
public class DrameFrame extends JFrame { //显示
linux的ps命令
被触发
linux
Linux中的ps命令是Process Status的缩写。ps命令用来列出系统中当前运行的那些进程。ps命令列出的是当前那些进程的快照,就是执行ps命令的那个时刻的那些进程,如果想要动态的显示进程信息,就可以使用top命令。
要对进程进行监测和控制,首先必须要了解当前进程的情况,也就是需要查看当前进程,而 ps 命令就是最基本同时也是非常强大的进程查看命令。使用该命令可以确定有哪些进程正在运行
Android 音乐播放器 下一曲 连续跳几首歌
肆无忌惮_
android
最近在写安卓音乐播放器的时候遇到个问题。在MediaPlayer播放结束时会回调
player.setOnCompletionListener(new OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
mp.reset();
Log.i("H
java导出txt文件的例子
知了ing
java servlet
代码很简单就一个servlet,如下:
package com.eastcom.servlet;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.sql.Connection;
import java.sql.Resu
Scala stack试玩, 提高第三方依赖下载速度
矮蛋蛋
scala sbt
原文地址:
http://segmentfault.com/a/1190000002894524
sbt下载速度实在是惨不忍睹, 需要做些配置优化
下载typesafe离线包, 保存为ivy本地库
wget http://downloads.typesafe.com/typesafe-activator/1.3.4/typesafe-activator-1.3.4.zip
解压r
phantomjs安装(linux,附带环境变量设置) ,以及casperjs安装。
alleni123
linux spider
1. 首先从官网
http://phantomjs.org/下载phantomjs压缩包,解压缩到/root/phantomjs文件夹。
2. 安装依赖
sudo yum install fontconfig freetype libfreetype.so.6 libfontconfig.so.1 libstdc++.so.6
3. 配置环境变量
vi /etc/profil
JAVA IO FileInputStream和FileOutputStream,字节流的打包输出
百合不是茶
java核心思想 JAVA IO操作 字节流
在程序设计语言中,数据的保存是基本,如果某程序语言不能保存数据那么该语言是不可能存在的,JAVA是当今最流行的面向对象设计语言之一,在保存数据中也有自己独特的一面,字节流和字符流
1,字节流是由字节构成的,字符流是由字符构成的 字节流和字符流都是继承的InputStream和OutPutStream ,java中两种最基本的就是字节流和字符流
类 FileInputStream
Spring基础实例(依赖注入和控制反转)
bijian1013
spring
前提条件:在http://www.springsource.org/download网站上下载Spring框架,并将spring.jar、log4j-1.2.15.jar、commons-logging.jar加载至工程1.武器接口
package com.bijian.spring.base3;
public interface Weapon {
void kil
HR看重的十大技能
bijian1013
提升 能力 HR 成长
一个人掌握何种技能取决于他的兴趣、能力和聪明程度,也取决于他所能支配的资源以及制定的事业目标,拥有过硬技能的人有更多的工作机会。但是,由于经济发展前景不确定,掌握对你的事业有所帮助的技能显得尤为重要。以下是最受雇主欢迎的十种技能。 一、解决问题的能力 每天,我们都要在生活和工作中解决一些综合性的问题。那些能够发现问题、解决问题并迅速作出有效决
【Thrift一】Thrift编译安装
bit1129
thrift
什么是Thrift
The Apache Thrift software framework, for scalable cross-language services development, combines a software stack with a code generation engine to build services that work efficiently and s
【Avro三】Hadoop MapReduce读写Avro文件
bit1129
mapreduce
Avro是Doug Cutting(此人绝对是神一般的存在)牵头开发的。 开发之初就是围绕着完善Hadoop生态系统的数据处理而开展的(使用Avro作为Hadoop MapReduce需要处理数据序列化和反序列化的场景),因此Hadoop MapReduce集成Avro也就是自然而然的事情。
这个例子是一个简单的Hadoop MapReduce读取Avro格式的源文件进行计数统计,然后将计算结果
nginx定制500,502,503,504页面
ronin47
nginx 错误显示
server {
listen 80;
error_page 500/500.html;
error_page 502/502.html;
error_page 503/503.html;
error_page 504/504.html;
location /test {return502;}}
配置很简单,和配
java-1.二叉查找树转为双向链表
bylijinnan
二叉查找树
import java.util.ArrayList;
import java.util.List;
public class BSTreeToLinkedList {
/*
把二元查找树转变成排序的双向链表
题目:
输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表。
要求不能创建任何新的结点,只调整指针的指向。
10
/ \
6 14
/ \
Netty源码学习-HTTP-tunnel
bylijinnan
java netty
Netty关于HTTP tunnel的说明:
http://docs.jboss.org/netty/3.2/api/org/jboss/netty/channel/socket/http/package-summary.html#package_description
这个说明有点太简略了
一个完整的例子在这里:
https://github.com/bylijinnan
JSONUtil.serialize(map)和JSON.toJSONString(map)的区别
coder_xpf
jquery json map val()
JSONUtil.serialize(map)和JSON.toJSONString(map)的区别
数据库查询出来的map有一个字段为空
通过System.out.println()输出 JSONUtil.serialize(map): {"one":"1","two":"nul
Hibernate缓存总结
cuishikuan
开源 ssh javaweb hibernate缓存 三大框架
一、为什么要用Hibernate缓存?
Hibernate是一个持久层框架,经常访问物理数据库。
为了降低应用程序对物理数据源访问的频次,从而提高应用程序的运行性能。
缓存内的数据是对物理数据源中的数据的复制,应用程序在运行时从缓存读写数据,在特定的时刻或事件会同步缓存和物理数据源的数据。
二、Hibernate缓存原理是怎样的?
Hibernate缓存包括两大类:Hib
CentOs6
dalan_123
centos
首先su - 切换到root下面1、首先要先安装GCC GCC-C++ Openssl等以来模块:yum -y install make gcc gcc-c++ kernel-devel m4 ncurses-devel openssl-devel2、再安装ncurses模块yum -y install ncurses-develyum install ncurses-devel3、下载Erang
10款用 jquery 实现滚动条至页面底端自动加载数据效果
dcj3sjt126com
JavaScript
无限滚动自动翻页可以说是web2.0时代的一项堪称伟大的技术,它让我们在浏览页面的时候只需要把滚动条拉到网页底部就能自动显示下一页的结果,改变了一直以来只能通过点击下一页来翻页这种常规做法。
无限滚动自动翻页技术的鼻祖是微博的先驱:推特(twitter),后来必应图片搜索、谷歌图片搜索、google reader、箱包批发网等纷纷抄袭了这一项技术,于是靠滚动浏览器滚动条
ImageButton去边框&Button或者ImageButton的背景透明
dcj3sjt126com
imagebutton
在ImageButton中载入图片后,很多人会觉得有图片周围的白边会影响到美观,其实解决这个问题有两种方法
一种方法是将ImageButton的背景改为所需要的图片。如:android:background="@drawable/XXX"
第二种方法就是将ImageButton背景改为透明,这个方法更常用
在XML里;
<ImageBut
JSP之c:foreach
eksliang
jsp forearch
原文出自:http://www.cnblogs.com/draem0507/archive/2012/09/24/2699745.html
<c:forEach>标签用于通用数据循环,它有以下属性 属 性 描 述 是否必须 缺省值 items 进行循环的项目 否 无 begin 开始条件 否 0 end 结束条件 否 集合中的最后一个项目 step 步长 否 1
Android实现主动连接蓝牙耳机
gqdy365
android
在Android程序中可以实现自动扫描蓝牙、配对蓝牙、建立数据通道。蓝牙分不同类型,这篇文字只讨论如何与蓝牙耳机连接。
大致可以分三步:
一、扫描蓝牙设备:
1、注册并监听广播:
BluetoothAdapter.ACTION_DISCOVERY_STARTED
BluetoothDevice.ACTION_FOUND
BluetoothAdapter.ACTION_DIS
android学习轨迹之四:org.json.JSONException: No value for
hyz301
json
org.json.JSONException: No value for items
在JSON解析中会遇到一种错误,很常见的错误
06-21 12:19:08.714 2098-2127/com.jikexueyuan.secret I/System.out﹕ Result:{"status":1,"page":1,&
干货分享:从零开始学编程 系列汇总
justjavac
编程
程序员总爱重新发明轮子,于是做了要给轮子汇总。
从零开始写个编译器吧系列 (知乎专栏)
从零开始写一个简单的操作系统 (伯乐在线)
从零开始写JavaScript框架 (图灵社区)
从零开始写jQuery框架 (蓝色理想 )
从零开始nodejs系列文章 (粉丝日志)
从零开始编写网络游戏 
jquery-autocomplete 使用手册
macroli
jquery Ajax 脚本
jquery-autocomplete学习
一、用前必备
官方网站:http://bassistance.de/jquery-plugins/jquery-plugin-autocomplete/
当前版本:1.1
需要JQuery版本:1.2.6
二、使用
<script src="./jquery-1.3.2.js" type="text/ja
PLSQL-Developer或者Navicat等工具连接远程oracle数据库的详细配置以及数据库编码的修改
超声波
oracle plsql
在服务器上将Oracle安装好之后接下来要做的就是通过本地机器来远程连接服务器端的oracle数据库,常用的客户端连接工具就是PLSQL-Developer或者Navicat这些工具了。刚开始也是各种报错,什么TNS:no listener;TNS:lost connection;TNS:target hosts...花了一天的时间终于让PLSQL-Developer和Navicat等这些客户
数据仓库数据模型之:极限存储--历史拉链表
superlxw1234
极限存储 数据仓库 数据模型 拉链历史表
在数据仓库的数据模型设计过程中,经常会遇到这样的需求:
1. 数据量比较大; 2. 表中的部分字段会被update,如用户的地址,产品的描述信息,订单的状态等等; 3. 需要查看某一个时间点或者时间段的历史快照信息,比如,查看某一个订单在历史某一个时间点的状态, 比如,查看某一个用户在过去某一段时间内,更新过几次等等; 4. 变化的比例和频率不是很大,比如,总共有10
10点睛Spring MVC4.1-全局异常处理
wiselyman
spring mvc
10.1 全局异常处理
使用@ControllerAdvice注解来实现全局异常处理;
使用@ControllerAdvice的属性缩小处理范围
10.2 演示
演示控制器
package com.wisely.web;
import org.springframework.stereotype.Controller;
import org.spring