前端(一)
1.1 前端工程化发展历史
前端发展历程
(1995)Brendan Eich发明Javascript -> (2005)Ajax广泛应用 -> (2008)V8引擎发布 ->(2009)Node.js发布 ->(2010)NPM 0.1版发布 -> (2013) Webpack 1.0版发布 -> (2013)React 1.0版发布 -> (2014) Vue 1.0版本发布
开发方式的演进
-
- (1995-2005) 石器时代:原始全栈开发,服务端渲染,少量食用JavaScript;
-
- (2005-2013) 农业社会:前后端分离,利用Ajax实现前后端分离、SPA模式萌芽;
-
- (2013-2014) 工业化:模块化开发兴起,npm包管理工具,webpack编译打包;
-
- (2014-Now) 现代化:基于Angular、React、Vue等框架
服务器端渲染
浏览器 ->(提交Form)–(服务器) -> (服务端语言JAVA、PHP、C#、Vb.net)<–>数据库/Service/–>(模板、JSP、CSS+JS)–(HTML) ->浏览器
前后端分离
- 前端任务单元通常以画面为单位;
浏览器 -> 前端(HTML、–>JS<–>(Ajax)–服务器(服务端语言(JAVA/PHP/C#/Vb.net)<–>数据库/Service)、CSS、其他静态资源)
组件化
- 任务单元变为功能模块,业务逻辑开始向前端转移。 开启大前端时代。
浏览器 -> 前端(HTML、–>Bundle.js<–>Ajax(服务器(服务端语言(JAVA/PHP/C#/Vb.net)<–>数据库/Service))、<–(打包)JS/CSS/其他静态资源)
主流前端技术栈
框架:Vue、React、Angular
语言:TS、ES6
工程化工具:Webpack、Vite、Rollup、Gulp
跨平台技术:Electron、React Native、Flutter、小程序
??前端工程化解决了哪些痛点?
- 模块化与共通化:解决了作用域命名冲突,充分代码复用;
- 统一编码规范,提升工程质量;
- 提高访问性能:合并压缩;
- 优雅降级:通过loader和plugin来实现低版本浏览器的兼容;
Web发展
Web 1.0
- 1994
- PC互联网
- 门户时代
- 被动接受内容
Web 2.0
- 2008
- 移动互联网
- 社交网络、O2O、手机游戏、短视频、网络直播、信息流服务、应用分发和互联网金融
- 自主创建内容
Web 3.0
- 去中心化
- 加密
- 用户可以真正拥有自己的数据
1.2 前端工程化的应用场景
-
- 解决从开发到发布全流程的问题
-
项目的核心三要素:时间、质量、成本
- 时间:现代项目尤其互联网项目的特点就是快,提升开发效率,在短时间内实现功能的迭代升级
- 质量:
- 代码质量:通过规范来实现,文档形式的规范和通过lint实现的工程化规范
- 性能:提升用户体验
- 成本:
- 模块化和共通化
- 自动发布
-
脚手架: react(create-react-app)、vue(vue-cli);
创建项目:开箱即用;热更新:所见即所得,修改马上生效;
JS的兼容:ES6、TS --> babel-loader -> ES5 – sass scss less -> css -webkit- -moz-
-
- 传统企业前端项目重构
- 传统企业中依然有很多的现行系统使用传统的前端技术栈:
- HTML + JS + Ajax
- 静态资源未优化
- 发布流程半自动化
-
- 现代企业项目升级
- 现代企业在成长的过程中会产生大“企业病”:
- 项目增加:新业务线,新的组织产生都会产生新的项目,这个增长不是简单的线性关系。
- 人员增多:开发团队人数增加,人员水平有差异;
- 复杂度变高:业务复杂度提升带来技术复杂度的提升,业务场景的变化也会产生跨终端的需求;
- pull request(pr) -> review -> merge
痛点
- 项目增多
- 项目模块化与共通化
- npm + webpack
- CommonJS
- 抽象业务需求,提炼组件库
- 研发脚手架
- 统一技术栈
- 统一编码规范
- 可配置的模板
- 统一发布流程,提高运维效率
- 项目模块化与共通化
- 人员增多
- 研发脚手架
- 降低学习成本
- 提升开发效率
- 提升构建速度
- 研发脚手架
- 技术复杂度增加
- 定制loader和plugin
- 定制组件库
1.3 前端工程化关键技术之模块化
什么是模块化?
- 模块化开发是将程序划分成一个个小的结构。
- 特点:
- 在结构中编写内部实现,它有自己的作用域,不会污染结构外的代码和作用域;
- 这个结构可以将自己希望暴露的变量、函数、对象等导出给其他结构使用,也可以通过某种方式,导入另外结构中的变量、函数、对象等等;
- 之前提到的结构,就是模块;按照这种结构划分开发程序的过程,就是模块化开发的过程
模块化思想的发展过程
1. function 阶段
- 按照功能将代码块拆分;容易产生作用域污染,需要人工避免命名冲突
2. 命名空间阶段
- 参考了面向对象思想,限制了作用域避免了命名冲突的问题;访问权限问题
3. 自运行匿名函数:解决数据访问权限问题
- 使用到闭包思想,来限制作用域;延伸阅读:闭包
4. 增强:支持导入依赖
- 需要注意模块的加载顺序;依赖模块过多时,代码可读性下降
1.4 CommonJS规范
1.CommonJS规范内容
每一个文件就是一个模块,拥有自己独立的作用域,变量,以及方法等,对其他的模块都不可见。
CommonJS规范规定,每个模块内部,module变量代表当前模块。这个变量是一个对象,它的exports属性(即module.exports)是对外的接口。
加载某个模块,其实是加载该模块的module.exports属性。require方法用于加载模块。
加载方式:同步加载
2.CommonJS特点
所有代码都运行在模块作用域,不会污染全局作用域;
模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。再想让模块再次运行,必须清除缓存。
模块加载的顺序,按照其在代码中出现的顺序;
模块输出的值是值的拷贝:从而控制了数据的访问权限;
3.require的内部处理流程
require命令是CommonJS规范之中,用来加载其他模块的命令。它其实不是一个全局命令,而是指向当前模块的module.require命令,而后者又调用Node的内部命令Module._load;
Module._load = function(request,parent,isMain){ //1.检查Module._cache,是否缓存之中有指定模块 //2.如果缓存之中没有,就创建一个新的Module实例 //3.将它保存到缓存; //4.使用module.load()加载指定的模块文件,读取文件内容后,使用module.compile()执行文件代码 //5.如果加载/解析过程报错,就从缓存删除该模块 //6.返回该模块的module.exports };
一旦require函数准备完毕,整个所要加载的脚本内容,就被放到一个新的函数之中,这样就可以避免污染全局环境。该函数的参数包括require、module、exports,以及其他一些参数。
(function(exports,require,module,__filename,__dirname){ //你的代码被导入在这里 });
4.在浏览器中使用CommonJS(browserify介绍)
不能在浏览器中直接使用,而需要借用以下工具;
npm install browserify -g
browserify inputPath.js -o outputPath.js —>browserify index.js->(输入文件名) -o bundle.js->(输出文件名) \eg: browserify index.js -o bundle.js
官网:https://browserify.org/
1.5 ESModule规范详解
1. AMD规范介绍
AMD全称是Asynchronous Modules Definition异步模块定义,提供定义模块及异步加载该模块依赖的机制,这和浏览器的异步加载模块的环境刚好适应(浏览器同步加载模块会导致性能、可用性、调试和跨域访问等问题)。
AMD规范只定义了一个函数“define”,它是全局变量。模块通过define函数定义在闭包中,格式如下:
define(id?String,dependencies?:String[],factory:Function|Object);
代表:require.js
2.CMD规范介绍
Common Module Definition, 通用模块定义。
异步加载,可以像在Node环境中一样来书写模块代码。代码的书写格式如下:
define(function(require,exports,module){
var $ = require('jquery');
exports.sayHello = function(){
$('#hello').toggle('slow');
};
});
代表:sea.js
3.ESModule介绍
在编译阶段确定依赖关系和输入输出。
export导出模块:export为普通导出、export default为默认导出;
import加载模块
特点:
- 每一个模块只加载一次,并执行一次,再次加载同一文件,直接从内存中读取;
- 每一个模块内声明的变量都是局部变量,不会污染全局作用域;
- 通过export导出模块,通过import导入模块
- ES6模块只支持静态导入和导出,只可以在模块的最外层作用域使用import和export
4.ESModule和CommonJS的区别
- CommonJS
- 导出值是值的拷贝
- 单个值导出
- 运行时加载
- 同步加载
- 模块中this指向当前模块- ESModule
- 导出值是值的引用
- 多个值导出
- 编译时加载
- 支持同步和异步加载
- 指向undefined
1.6 npm+webpack原理
1.npm介绍
NPM的全称时Node Package Manager, 是一个NodeJS包管理和分发工具,已经成为了非官方的发布Node模块(包)的标准。
官网:https://www.npmjs.com/
特点:
所有模块都在仓库中集中管理,统一分发使用
在package.json文件中记录模块信息,依赖关系等
通过publish命令发布到仓库
通过install进行按照
2.npm的使用和原理
npm init: 创建项目 npm init -y
npm install: 安装模块 npm install -S/生产环境和开发环境中都可用的包 -D/只开发环境中使用的包
npm publish: 上传到仓库(public/private)
npm run xxx: 执行指定的脚本
npm info: 查看包信息
npm ls xxx: 查看安装的包的版本号
npm config: 配置信息 npm config -help
npm config get registry: 查看当前使用的镜像
npm config set registry “https://r.cnpmjs.com”: 修改镜像源
新建.npmrc文件: 可以在项目里配置该项目适用的特殊项,eg: registry=“https://www.baidu.com”
3.webpack介绍
- 发展历史:
- 2012年3月10号诞生
- 作者德国人Tobias(Java工程师),从事将Java转为js的研究(GWT)-(谷歌web工具库),里面有个特性叫“code splitting”
- "code splitting"就是Webpack现在提供的主要功能
- 原理:
Webpack 是一个前端资源加载、打包工具。它将根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源。
Webpack 可以将多种静态资源js、css、less转换成一个静态文件,从而减少了页面的请求。
- 核心概念:
- Entry:告诉webpack起点在哪,也就是入口文件
- Output: 告诉webpack终点在哪,哪里输出,怎么命名
- Module:在webpack里一切皆模块,一个模块对应一个文件,会根据Entry开始递归查找所有的文件
- Chunk: 代码块,一个Chunk由多个模块组合而成,用于代码合并与分割
- Loader: 让webpack可以处理非js文件,因为webpack本身只理解js,loader可以将所有类型的文件转换为webpack能过处理的有效模块,再通过webpack的打包功能处理;
4.webpack的Hello World
webpack.config.js是webpack的配置文件
module.exports = {
mode: "development",//"development"|"production"|"none"
entry: "./app.js",
output: {
path: __dirname,
filename: "./bundle2.js",
}
}
2.1 工程化场景分析
1.案例分析
- App瘦身 400MB -> 200MB
- eg:
js -> rich editor -> template 40MB 修改库:修改代码中的引用 fork -> 修改源代码 —> || repo -> 修改源代码 -> 1. npm install git+// 2. npm publish @test/richEditor npm install @test/richEditor
license 协议:MIT/- 使用COS(存储桶)实现CDN加速(devOps VS 前端工程化)
- jenkins(触发器)/ -> gitlab && github
- rollup
3.1 webpack 基本使用带入
webpack官网:https://webpack.js.org/
- 为什么使用webpack
在web浏览器中运行Javascript有两种方法。第一种方式,引用一些脚本来存放每个功能;此方案特别难扩展,因为加载太多脚本会导致网络瓶颈。第二种方式,使用一个包含所有代码的大型.js文件,但是这个方法会导致作用域,文件大小,可读性,可维护性等方面的问题出现。
- 依赖自动收集
传统的任务构建工具基于Google的Closure编译器,都要求用户手动在顶部声明所有的依赖。然而像webpack一类的打包工具自动构建并基于用户所引导或导出的内容推断出依赖的图谱。这个特性与其他的如:plugin and loader一道让开发者用户体验更好。
- webpack主要功能:
- 前端模块化开发支持(解决作用域污染等问题,提高代码可维护性)
- 代码压缩混淆(减少传输时间,加快页面打开速度,保护源代码)
- 处理浏览器JS兼容问题(ES5、ES6等多版本兼容)
- 性能优化(SEO)
- webpack基本配置
mkdir webpack-demo
cd webpack-demo
npm init -y
npm install webpack webpack-cli --save-dev
```
- Webpack.config.js
const path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'main.js', path: path.resolve(__dirname,'dist'), } }
- 运行
npx webpack --config webpack.config.js
- 常用plugin简介
-
- HotModuleReplacementPlugin -D (开发时使用的模块) -> 模块热更新
-
- clean-webpack-plugin -> 目录清理
-
- html-webpack-plugin -> 自动生成一个index.html文件,将打包的js文件自动通过script标签
-
- uglifyjs-webpack-plugin -> js压缩
-
- mini-css-extract-plugin -> 分离样式文件,CSS提取为独立的文件
-
- webpack-bundle-analyzer -> 可视化Webpack输出文件的体积(业务组件、依赖第三方模块)
-
- DefinePlugin -> 定义全局常量,应用:为开发和生产环境引入不用的配置;eg如下:
js plugins:[new webpack.DefinePlugin({ HOST:JSON.stringify("https://api.dev.com"), })]
- DefinePlugin -> 定义全局常量,应用:为开发和生产环境引入不用的配置;eg如下:
- 常用loader简介
-
- style-loader -> 用于将css文件注入到index.html中的style标签上,通常与css-loader一起使用
-
- css-loader -> 用于处理css文件,使得在js文件中引入使用,通常与style-loader一起使用
-
- less-loader -> 处理less代码
-
- sass-loader -> 处理sass代码
-
- babel-loader -> 把ES6转为ES5
-
- ts-loader -> 把Typescript转换成ES5
-
- file-loader -> 打包图片,打包字体图标
-
- url-loader -> 和file-loader类似,但是当文件小于设定的limit时,可以返回一个DataUrl(提升网页性能)
-
- html-withimg-loader -> 打包HTML文件中的图片
-
- eslint-loader -> 用于检查常见的JavaScript代码错误,也可以进行“代码规范”检查
- 打包优化 - Code Splitting 代码分割
没有优化的原始配置
const path = require('path');
module.exports = {
mode: 'development',
entry: {
index: './src/index.js',
another: './src/another-entryModule.js',
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname,'dist'),
},
};
防止重复引入(Prevent Duplication)
- 1.使用shared属性
js const path = require('path'); module.exports = { mode: 'developoment', entry: { index: { import: './src/index.js', dependOn: 'shared', }, another: { import: './src/another-entryModule.js', dependOn: 'shared', }, shared: 'lodash', }, output: { filename: '[name].bundle.js', path: path.resolve(__dirname,'dist'), }, }
- 2.使用SplitChunksPlugin
js const path = require('path'); module.exports = { mode: 'development', entry: { index: './src/index.js', another: './src/another-entryModule.js', }, output:{ filename: '[name].bundle.js', path: path.resolve(__dirname,'dist'), }, optimization: { splitChunks: { chunks: 'all', }, }, };
- 3.动态导入(Dynamic Imports)
使用异步方式导入模块
js //优化前 import _ from 'lodash'; //优化后 //在需要使用的地方去import const { default: _ } = await import('lodash');
- 4.Caching缓存
contenthash
文件名可以添加属性[contenthash],当文件内容改变时会跟着一同改变,防止文件被缓存。
module.exports = {
entry: './src/index.js',
plugins: [
new HtmlWebpackPlugin({
title: 'Output Management',
title: 'Caching',
}),
],
output: {
filename: '[name].[contenthash].js',
path: path.resolve(__dirname,'dist'),
clean: true,
},
};
- 5.提取文件范例(Extracting Boilerplate)
webpack 提供了一个优化功能,可使用optimization.runtimeChunk选项将runtime代码拆分为一个单独的chunk。将其设置为single来为所有chunk创建一个runtime bundle:
module.exports = {
entry: './scr/index.js',
plugins: [
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({title:'Caching'}),
],
output: {
filename: '[name].[contenthash].js',
path: path.resolve(__dirname,'dist'),
},
optimization: {
runtimeChunk: 'single'
}
}
- Webpack 打包文件分析
- webpack_modules
模块定义,键值对
key是文件路径
value是通过闭包加载js文件中的内容
- webpack_module_cache
模块的缓存,确保模块只加载一次
- webpack_require
相当于CommonJS规范中的require函数
- webpack_require.r
在exports中定义_esModule属性,值为true
- webpack_require.d
在exports上挂载要导出的属性和方法
- webpack_require.o(obj,attr)
判断obj对象是否包含指定的属性attr
- Webpack ast 语法分析
什么是ast
ast:抽象语法树(abstract syntax tree),是源代码的抽象语法结构的树状表现形式。树上的每个节点都表示源代码中的一个结构。
之所以说语法是【抽象】的,是因为这里的语法并不会表示出真实语法中出现的每个细节。使用场景:
- 语法解析,检查
- 代码格式化,高亮,错误提示,代码自动补全
- Babel编译ES6语法
- 代码压缩,混淆代码
- 可以编写有独特语法特征的高级框架,例如react,vue等
ast的转换工作流程
源代码 --> 原语法树 --> 遍历语法树上的各个节点 --> 对语法树进行修改转换 --> 新的语法树 --> 根据新的语法树重新生成代码
做一个ast语法树
- 分词(tokenize)
将一行行的源码拆解成一个个token。所谓token,是指语法上不可能再分,最小的单个字符或字符串。
6 * 7 的语法树
var userName = “油茶”
var(关键字keyword) useName(标识符identifier) =(赋值assignment) “油茶”(字符串Literal)
- 实际使用:将function的驼峰命名转换为下划线命名
需要使用到的库
- esprima: 解析器,将源代码转换成抽象语法树
- estraverse: 用来遍历抽象语法树的方法
- escodegen: 将转换后的语法树,重新生成源码
驼峰命名转换为下划线的方法:
str.replace(/[A-Z]/g,(a)=>`_${a.toLowerCase()}`);
源代码
function weWantMoney(){}
实战:
npm init -y
// 这里我用的是yarn安装
yarn add esprima estraverse escodegen -D
// 创建输入文件input.js
function weWantMoney(){
console.log('$$$');
}
// 创建index.js文件
const esprima = require('esprima');
const estraverse = require('estraverse');
const escodegen = require('escodegen');
const fs = require('fs');
const inputSoure = fs.readFileSync('./input.js').toString();
// 把代码转换成ast树形结构
const astTree = esprima.parseModule(inputSoure);
console.log(astTree);
// 遍历树上的所有结点
estraverse.traverse(astTree,{
enter: (node) => {
console.log('----node.type',node.type);
if(node.type === 'FunctionDeclaration') {
node.id.name = node.id.name.replace(/[A-Z]/g,(a)=>`_${a.toLowerCase()}`)
}
},
leave: (node) => {
console.log('---leave');
}
})
//把ast树形结构,转换为代码
const newCode = escodegen.generate(astTree);
console.log('---result',newCode)
fs.writeFileSync('./output.js',newCode)
阅读源代码
- webpack是使用acornjs来解析把js解析成ast tree的。
acornjs 3792行- webpack是如何解析css语法树的:
CssParser.js 119行parse方法阅读几个核心function
- eatWhiteLine: 删除空白行
- eatUntil: 删除符合条件之前的内容
- eatText: 删除注释并返回新字符串
如何提取css中的url属性
walkCssTokens.js 592行
- 调用关系:
// 开始每个字符串进行检查
CssParser.walkCssTokens
// 当碰到字母u的时候 288行
CHAR_MAP[cc](input,pos,callbacks)
···
case CC_LOWER_U:
return consumePotentialUrl;
···
// 从pos位置开始进行字符串提取
consumePotentialUrl
// 其余完成之后交给回调函数来处理
CssParser_url
- webpack loader 核心机制分析
什么是loader?
loader 是一个函数,通过它我们可以在webpack处理特定资源(文件)之前进行预处理。
例如: webpack仅仅只能识别javascript模块,而我们在使用TypeScript编写代码时可以提取通过babel-loader将.ts后缀文件提取编译称为JavaScript代码,之后再交给Webpack处理。
常用配置参数
- 基础配置示例
module.exports = {
module: {
rules: [
{ test: /.css$/, use: 'css-loader', enforce: 'post' },
{ test: /.ts$/, use: 'ts-loader' },
],
},
};
- 参数
- test
test是一个正则表达式,根据test的规则去匹配文件。如果匹配到,那么该文件就会交给对应的loader去处理。
- use
use表示匹配到test中匹配对应的文件应该使用哪个loader的规则去处理,use值的类型:字符串或数组。
当为数组时,表执行的顺序为<从右往左(从下往上)>的。 - enforce
pre: 前置执行
post: 后置执行
下面例子的执行顺序为:
(《sass-loader》 --> 《css-loader》 --> 《style-loader》)
```js
module.exports = {
module: {
rules: [
{ test: /.css$/, use: 'sass-loader', enforce: 'pre' },
{ test: /.css$/, use: 'css-loader' },
{ test: /.css$/, use: 'style-loader', enforce: 'post' },
],
},
};
```
loader的执行顺序
(《pre-loader》 --> 《normal-loader》 --> 《inline-loader》 --> 《post-loader》)
pitch 的执行顺序
(《post-loader》 <-- 《inline-loader》 <-- 《normal-loader》 <-- 《pre-loader》)
( (loader) <-- (loader) <-- (loader) <-- (loader) ) <-- read resource
(《pre-loader》 <-- 《normal-loader》 <-- 《inline-loader》 <-- 《post-loader》)
read resource <-- pitch <-- pitch <-- pitch <-- pitch
inline loader举例:
- 使用!前缀,将禁用所有已配置的 normal loader
import Styles from '!style-loader!css-loader?modules!./styles.css';
- 使用!!前缀,将禁用所有已配置的loader (pre loader,normal loader,post loader)
import Styles from '!!style-loader!css-loader?modules!./styles.css';
- 使用-!前缀,将禁用所有已配置的 pre loader 和 loader,但是不禁用 post loader
import Styles from '-!style-loader!css-loader?modules!./styles.css';
关于pitch阶段的说明
loader的执行阶段分为两个阶段:
- webpack 在使用loader处理资源时首先会经过loader.pitch阶段
- pitch 阶段结束后才会读取文件而后进行normal阶段处理。
Pitch 阶段:loader上的pitch方法,按照 后置(post)、行内(inline)、普通(normal)、前置(pre) 的顺序调用。
Normal 阶段:loader上的常规方法,按照前置 (pre)、普通(normal)、行内(inline)、后置(post) 的顺序调用。
function loader(){
// 正常的loader执行阶段...
}
// remainingRequest:表示剩余需要处理的loader的绝对路径,如果多个路径以!为分割组成的字符串
// precedingRequest: 表示pitch阶段已经迭代过的loader,如果多个路径按照!分割组成的字符串
// data: 在pitch和loader之间交互数据
loader.pitch = function (remainingRequest, precedingRequest, data) {
// pitch loader
// 如果return了非undefined的值,会带来熔断效果
}
如果loader.pitch函数中return了非undefined的值,会带来熔断效果。如下效果:
loader -1 | loader -2 | loader -3 | loader -4 | loader -5
loader <— loader —-— loader —x— loader —x— loader
pitch —> pitch —> pitch(发生熔断) —> loader(回传到 loader -2 的loader),熔断后loader-4/5都不会执行
loader的种类
- 1.同步loader
module.exports = function(content,map,meta){ return someSyncOperation(content); }
- 2.异步loader
两种常见写法:function asyncLoader() { return Promise((resolve) => { // do something // resolve的值相当于同步loader的返回值 resolve('this is the result value') }) } function asyncLoader() { const callback = this.async() // do something // 结束运行并把值返回 callback('this is the result value') }
- "Raw"loader
默认情况下,资源文件会被转化为UTF-8字符串,然后传给loader。通过设置属性raw为true,loader可以接受这个图片文件或者视频文件原始的Buffer,之后对其进行处理。每一个loader都可以用String或者Buffer的形式传递它的处理结果。Complier这个编译器,将会把它们在loader之间相互转换。
loader源码解析
为了更好的理解pitch的作用,可以看一下style-loader的代码。
style-loader org
style-loader
loader.pitch生成的代码:
module.exports.pitch = function (request) {
return `
import API from"!../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js";
import domAPI from"!../node_modules/style-loader/dist/runtime/styleDomAPI.js";
import insertFn from"!../node_modules/style-loader/dist/runtime/insertBySelector.js";
import setAttributes from"!../node_modules/style-loader/dist/runtime/setAttributesWithAttributes.js";
import insertStyleElement from"!../node_modules/style-loader/dist/runtime/insertStyleElement.js";
import styleTagTransformFn from"!../node_modules/style-loader/dist/runtime/styleTagTransform.js";
import content, * as namedExport from"!!../node_modules/css-loader/dist/cjs.js!./style.css";
var options = {};
options.styleTagTransform = styleTagTransformFn;
options.setAttributes = setAttributes;
options.insert = insertFn.bind(null,"head");
options.domAPI = domAPI;
options.insertStyleElement = insertStyleElement;
var update = API(content,options);
`;
}