웹 프론트엔드 JS 튜토리얼 Parcel 소스 코드의 상세 분석(예제 포함)

Parcel 소스 코드의 상세 분석(예제 포함)

Mar 18, 2019 am 10:41 AM
javascript 프런트 엔드 소스 코드

이 기사는 Parcel 소스 코드에 대한 자세한 분석을 제공합니다(예제 포함). 도움이 필요한 친구들이 참고할 수 있기를 바랍니다.

이 글은 Parce의 소스 코드와 기본 코드 구조 및 실행 과정을 분석한 글입니다. 이전에 Parcel에 대해 잘 모르셨던 분들은 먼저 Parcel 공식 홈페이지에 접속하셔서 알아보시면 됩니다

소개

다음은 게으른 분들을 위해 공식 웹사이트에서 복사한 소개입니다.

매우 빠른 제로 구성 웹 애플리케이션 패키징 도구, 매우 빠른 패키징

Parcel은 작업자 프로세스를 사용하여 멀티 코어 컴파일을 가능하게 합니다. 빌드를 다시 시작한 후에도 빠른 재컴파일을 가능하게 하는 파일 시스템 캐시도 있습니다.

모든 리소스를 패키징하세요

Parcel은 JS, CSS, HTML, 파일 등에 대한 기본 지원을 제공하며 플러그인이 필요하지 않습니다.

자동 변환

Babel, PostCSS, PostHTML 및 심지어 node_modules 패키지도 필요한 경우 코드를 자동으로 변환하는 데 사용됩니다.

제로 구성 코드 분할

동적 가져오기() 구문을 사용하여 출력 파일을 Parcel로 묶습니다. 번들)이므로 초기 로드 시 필요한 코드만 로드하면 됩니다.

핫 모듈 교체

Parcel에는 구성이 필요하지 않습니다. 개발 환경에서는 코드가 변경되면 모듈이 브라우저에서 자동으로 업데이트됩니다.

친절한 오류 로그

오류가 발생하면 Parcel은 문제를 찾는 데 도움이 되도록 구문이 강조 표시된 코드 조각을 출력합니다.

포장 도구 Time
browserify 22.98s
webpack 20.71s
par cel 9.98s
소포 - 캐시 포함 2.64초

Packaging tool

일반적으로 사용되는 패키징 도구의 일반 기능:

모듈화(코드 분할, 병합, 트리 쉐이킹 등) 컴파일(es6, 7 ,8 sass typescript 등) 압축(이미지 압축을 포함한 js, css, html) HMR(핫 교체)

version

parcel-bundler 버전: #🎜🎜 ## 🎜🎜#"버전": "1.11.0"

파일 구조

|-- assets          资源目录 继承自 Asset.js
|-- builtins        用于最终构建
|-- packagers       打包
|-- scope-hoisting  作用域提升 Tree-Shake
|-- transforms      转换代码为 AST
|-- utils           工具
|-- visitors        遍历 js AST树 收集依赖等

|-- Asset.js          资源
|-- Bundle.js         用于构建 bundle 树
|-- Bundler.js        主目录  
|-- FSCache.js        缓存
|-- HMRServer.js      HMR服务器提供 WebSocket
|-- Parser.js         根据文件扩展名获取对应 Asset
|-- Pipeline.js       多线程执行方法
|-- Resolver.js       解析模块路径
|-- Server.js         静态资源服务器
|-- SourceMap.js      SourceMap
|-- cli.js            cli入口 解析命令行参数
|-- worker.js         多线程入口
로그인 후 복사
프로세스

설명# 🎜🎜#

Parcel은 리소스 지향적입니다. JavaScript, CSS 및 HTML은 모두 webpack의 js가 이러한 파일 및 모듈의 종속성을 자동으로 분석하는 것은 아닙니다. 그런 다음 번들 트리를 빌드하고 패키지하여 지정된 디렉터리에 출력합니다

간단한 예

내부를 이해하기 위해 간단한 예부터 시작합니다. 소포#🎜의 소스 코드 및 프로세스 🎜#
index.html
  |-- index.js
    |-- module1.js
    |-- module2.js
로그인 후 복사

위는 예제의 구조이며, 입구는 index.html이고, index.html에서는 스크립트 태그를 사용하여 src/index.js를 참조합니다. .js에서는 2개의 하위 모듈을 소개합니다. #

cli

"bin": {
    "parcel": "bin/cli.js"
}
로그인 후 복사
parcel-bundler의 package.json을 확인하여 bin/cli.js를 찾고 ../src/cli

const program = require('commander');

program
  .command('serve [input...]') // watch build
  ...
  .action(bundle);

program.parse(process.argv);

async function bundle(main, command) {
  const Bundler = require('./Bundler');

  const bundler = new Bundler(main, command);

  if (command.name() === 'serve' && command.target === 'browser') {
    const server = await bundler.serve();

    if (server && command.open) {...启动自动打开浏览器}
  } else {
    bundler.bundle();
  }
}
로그인 후 복사
in cli를 가리킵니다. .js 커맨더를 사용하여 cli.js 명령줄에서 구문 분석하고 번들 메소드 호출

번들 함수를 호출하기 위한 Serve, Watch, Build 3가지 명령어가 있습니다. pracel index.html 실행 시 기본값은 Serve입니다. 그래서

Enter Bundler.js

bundler.serve

async serve(port = 1234, https = false, host) {
    this.server = await Server.serve(this, port, host, https);
    try {
      await this.bundle();
    } catch (e) {}
    return this.server;
  }
로그인 후 복사

bundler.serve 메소드가 호출됩니다. 최종 패키지 폴더를 가리키는 정적 서비스

다음은 중요한 번들 방법입니다.


bundler.bundle

async bundle() {
    // 加载插件 设置env 启动多线程 watcher hmr
    await this.start();

    if (isInitialBundle) {
      // 创建 输出目录
      await fs.mkdirp(this.options.outDir);

      this.entryAssets = new Set();
      for (let entry of this.entryFiles) {
          let asset = await this.resolveAsset(entry);
          this.buildQueue.add(asset);
          this.entryAssets.add(asset);
      }
    }

    // 打包队列中的资源
    let loadedAssets = await this.buildQueue.run();

    // findOrphanAssets 获取所有资源中独立的没有父Bundle的资源
    let changedAssets = [...this.findOrphanAssets(), ...loadedAssets];

    // 因为接下来要构建 Bundle 树,先对上一次的 Bundle树 进行 clear 操作
    for (let asset of this.loadedAssets.values()) {
      asset.invalidateBundle();
    }

    // 构建 Bundle 树
    this.mainBundle = new Bundle();
    for (let asset of this.entryAssets) {
      this.createBundleTree(asset, this.mainBundle);
    }

    // 获取新的最终打包文件的url
    this.bundleNameMap = this.mainBundle.getBundleNameMap(
      this.options.contentHash
    );
    // 将代码中的旧文件url替换为新的
    for (let asset of changedAssets) {
      asset.replaceBundleNames(this.bundleNameMap);
    }

    // 将改变的资源通过websocket发送到浏览器
    if (this.hmr && !isInitialBundle) {
      this.hmr.emitUpdate(changedAssets);
    }

    // 对资源打包
    this.bundleHashes = await this.mainBundle.package(
      this,
      this.bundleHashes
    );

    // 将独立的资源删除
    this.unloadOrphanedAssets();

    return this.mainBundle;
  }
로그인 후 복사

this.start를 단계별로 살펴보겠습니다#🎜 🎜#

start

if (this.farm) {
  return;
}

await this.loadPlugins();

if (!this.options.env) {
  await loadEnv(Path.join(this.options.rootDir, 'index'));
  this.options.env = process.env;
}

if (this.options.watch) {
  this.watcher = new Watcher();
  this.watcher.on('change', this.onChange.bind(this));
}

if (this.options.hmr) {
  this.hmr = new HMRServer();
  this.options.hmrPort = await this.hmr.start(this.options);
}

this.farm = await WorkerFarm.getShared(this.options, {
  workerPath: require.resolve('./worker.js')
  });
로그인 후 복사
start:# 🎜🎜#

시작 부분의 판단은 다중 실행을 방지합니다. 즉, this.start는 loadPlugins를 한 번만 실행하여 플러그인을 로드합니다. package.json 파일의 종속성 및 devDependency에서 Parcel-plugin-으로 시작하는 플러그인을 찾고 loadEnv를 호출하여 환경을 로드하고 dotenv, dotenv-expand 패키지를 사용하여 env.development를 확장합니다. .local, .env.development, .env.local, .env에서 process.envwatch로 청취 파일을 초기화하고 변경 콜백 함수를 바인딩하고 내부적으로 child_process.fork 프로세스를 생성하고, chokidar 패키지를 사용하여 파일 변경 사항을 모니터링합니다. hmr 시작 WebSocket은 변경된 리소스를 브라우저 팜으로 보냅니다. 여러 프로세스를 초기화하고 werker 작업 파일을 지정하고, 여러 child_processes를 열어 리소스를 구문 분석하고 컴파일합니다.

Next 번들로 돌아가서, isInitialBundle은 출력 폴더

를 생성하기 위해

fs.mkdirp를 빌드하는 것은 처음입니다. index.html이고 확장에 따라 얻는 것은 HTMLAsset입니다)

대기열에 자산 추가

그런 다음 this.buildQueue.run()을 시작하여 입구에서 리소스를 재귀적으로 패키징합니다#🎜🎜 #

PromiseQueue

여기서 buildQueue는 PromiseQueue 비동기 대기열입니다.

PromiseQueue는 초기화 중에 콜백 함수 콜백을 전달하고, 내부적으로 매개변수 대기열을 유지하고, 매개변수를 대기열에 푸시합니다. 실행시 큐 콜백(...queue.shift())을 순회하면서 큐가 모두 실행되고 Promise가 완료(해결됨)로 설정됩니다(Promise.all로 이해될 수 있음)

콜백 여기에 정의된 함수는 processAsset이고 매개변수는 항목 파일 index.html의 HTMLAsset

async processAsset(asset, isRebuild) {
  if (isRebuild) {
    asset.invalidate();
    if (this.cache) {
      this.cache.invalidate(asset.name);
    }
  }

  await this.loadAsset(asset);
}
로그인 후 복사

processAsset 함수입니다. 먼저 Rebuild인지, 첫 번째 빌드인지, 아니면 기반 빌드인지 확인합니다. 모니터링 파일 변경 사항을 확인하세요. 재구축인 경우 리소스는 속성이 재설정되고 캐시가 유효하지 않습니다
그 후 loadAsset을 호출하여 리소스를 로드하고 리소스를 컴파일합니다

loadAsset#🎜 🎜#

async loadAsset(asset) {
    if (asset.processed) {
      return;
    }

    // Mark the asset processed so we don't load it twice
    asset.processed = true;

    // 先尝试读缓存,缓存没有在后台加载和编译
    asset.startTime = Date.now();
    let processed = this.cache && (await this.cache.read(asset.name));
    let cacheMiss = false;
    if (!processed || asset.shouldInvalidate(processed.cacheData)) {
      processed = await this.farm.run(asset.name);
      cacheMiss = true;
    }

    asset.endTime = Date.now();
    asset.buildTime = asset.endTime - asset.startTime;
    asset.id = processed.id;
    asset.generated = processed.generated;
    asset.hash = processed.hash;
    asset.cacheData = processed.cacheData;

    // 解析和加载当前资源的依赖项
    let assetDeps = await Promise.all(
      dependencies.map(async dep => {
          dep.parent = asset.name;
          let assetDep = await this.resolveDep(asset, dep);
          if (assetDep) {
            await this.loadAsset(assetDep);
          }
          return assetDep;
      })
    );

    if (this.cache && cacheMiss) {
      this.cache.write(asset.name, processed);
    }
  }
로그인 후 복사

loadAsset 반복 컴파일을 방지하기 위해 처음에 판단이 있습니다#🎜 🎜# 그런 다음 캐시를 읽습니다. 읽기에 실패하면 this.farm.run을 호출하여 여러 프로세스에서 리소스를 컴파일합니다 #🎜🎜. # 컴파일 후 종속 파일을 로드하고 컴파일 결국 새 리소스가 사용되지 않으면 캐시만 재설정하면 됩니다

여기서 관련된 두 가지에 대해 이야기해 보겠습니다. 캐시 FSCache와 다중 프로세스 WorkerFarm

FSCache

read 读取缓存,并判断最后修改时间和缓存的修改时间
write 写入缓存

Parcel 소스 코드의 상세 분석(예제 포함)

缓存目录为了加速读取,避免将所有的缓存文件放在一个文件夹里,parcel 将 16进制 两位数的 256 种可能创建为文件夹,这样存取缓存文件的时候,将目标文件路径 md5 加密转换为 16进制,然后截取前两位是目录,后面几位是文件名

WorkerFarm

在上面 start 里初始化 farm 的时候,workerPath 指向了 worker.js 文件,worker.js 里有两个函数,init 和 run
WorkerFarm.getShared 初始化的时候会创建一个 new WorkerFarm ,调用 worker.js 的 init 方法,根据 cpu 获取最大的 Worker 数,并启动一半的子进程
farm.run 会通知子进程执行 worker.js 的 run 方法,如果进程数没有达到最大会再次开启一个新的子进程,子进程执行完毕后将 Promise状态更改为完成
worker.run -> pipeline.process -> pipeline.processAsset -> asset.process
Asset.process 处理资源:

async process() {
    if (!this.generated) {
      await this.loadIfNeeded();
      await this.pretransform();
      await this.getDependencies();
      await this.transform();
      this.generated = await this.generate();
    }

    return this.generated;
  }
로그인 후 복사

将上面的代码内部扩展一下:

async process() {
  // 已经有就不需要编译
  if (!this.generated) {
    // 加载代码
    if (this.contents == null) {
      this.contents = await this.load();
    }
    // 可选。在收集依赖之前转换。
    await this.pretransform();
    // 将代码解析为 AST 树
    if (!this.ast) {
      this.ast = await this.parse(this.contents);
    }
    // 收集依赖
    await this.collectDependencies();
    // 可选。在收集依赖之后转换。
    await this.transform();
    // 生成代码
    this.generated = await this.generate();
  }

  return this.generated;
}

// 最后处理代码
async postProcess(generated) {
  return generated
}
로그인 후 복사

processAsset 中调用 asset.process 生成 generated 这个generated 不一定是最终代码 ,像 html里内联的 script ,vue 的 html, js, css,都会进行二次或多次递归处理,最终调用 asset.postProcess 生成代码

Asset

下面说几个实现

HTMLAsset:

pretransform 调用 posthtml 将 html 解析为 PostHTMLTree(如果没有设置posthtmlrc之类的不会走)

parse 调用 posthtml-parser 将 html 解析为 PostHTMLTree

collectDependencies 用 walk 遍历 ast,找到 script, img 的 src,link 的 href 等的地址,将其加入到依赖

transform htmlnano 压缩代码

generate 处理内联的 script 和 css

postProcess posthtml-render 生成 html 代码

JSAsset:

pretransform 调用 @babel/core 将 js 解析为 AST,处理 process.env

parse 调用 @babel/parser 将 js 解析为 AST

collectDependencies 用 babylon-walk 遍历 ast, 如 ImportDeclaration,import xx from 'xx' 语法,CallExpression 找到 require调用,import 被标记为 dynamic 动态导入,将这些模块加入到依赖

transform 处理 readFileSync,__dirname, __filename, global等,如果没有设置scopeHoist 并存在 es6 module 就将代码转换为 commonjs,terser 压缩代码

generate @babel/generator 获取 js 与 sourceMap 代码

VueAsset:

parse @vue/component-compiler-utils 与 vue-template-compiler 对 .vue 文件进行解析

generate 对 html, js, css 处理,就像上面说到会对其分别调用 processAsset 进行二次解析

postProcess component-compiler-utils 的 compileTemplate, compileStyle处理 html,css,vue-hot-reload-api HMR处理,压缩代码

回到 bundle 方法:

let loadedAssets = await this.buildQueue.run() 就是上面说到的PromiseQueue 和 WorkerFarm 结合起来:buildQueue.run —> processAsset -> loadAsset -> farm.run -> worker.run -> pipeline.process -> pipeline.processAsset -> asset.process,执行之后所有资源编译完毕,并返回入口资源loadedAssets就是 index.html 对应的 HTMLAsset 资源

之后是 let changedAssets = [...this.findOrphanAssets(), ...loadedAssets] 获取到改变的资源

findOrphanAssets 是从所有资源中查找没有 parentBundle 的资源,也就是独立的资源,这个 parentBundle 会在等会的构建 Bundle 树中被赋值,第一次构建都没有 parentBundle,所以这里会重复入口文件,这里的 findOrphanAssets 的作用是在第一次构建之后,文件change的时候,在这个文件 import了新的一个文件,因为新文件没有被构建过 Bundle 树,所以没有 parentBundle,这个新文件也被标记物 change

invalidateBundle 因为接下来要构建新的树所以调用重置所有资源上一次树的属性

createBundleTree 构建 Bundle 树:

首先一个入口资源会被创建成一个 bundle,然后动态的 import() 会被创建成子 bundle ,这引发了代码的拆分。

当不同类型的文件资源被引入,兄弟 bundle 就会被创建。例如你在 JavaScript 中引入了 CSS 文件,那它会被放置在一个与 JavaScript 文件对应的兄弟 bundle 中。

如果资源被多于一个 bundle 引用,它会被提升到 bundle 树中最近的公共祖先中,这样该资源就不会被多次打包。

Bundle:

type:它包含的资源类型 (例如:js, css, map, ...)

name:bundle 的名称 (使用 entryAsset 的 Asset.generateBundleName() 生成)

parentBundle:父 bundle ,入口 bundle 的父 bundle 是 null

entryAsset:bundle 的入口,用于生成名称(name)和聚拢资源(assets)

assets:bundle 中所有资源的集合(Set)

childBundles:所有子 bundle 的集合(Set)

siblingBundles:所有兄弟 bundle 的集合(Set)

siblingBundlesMap:所有兄弟 bundle 的映射 Map

offsets:所有 bundle 中资源位置的映射 Map ,用于生成准确的 sourcemap 。

我们的例子会被构建成:

html            ( index.html )
  |-- js        ( index.js, module1.js, module2.js )
    |-- map     ( index.js, module1.js, module2.js )
로그인 후 복사

module1.js 和 module2.js 被提到了与 index.js 同级,map 因为类型不同被放到了 子bundle

一个复杂点的树:

// 资源树
index.html
  |-- index.css
  |-- bg.png
  |-- index.js
    |-- module.js
로그인 후 복사
// mainBundle
html            ( index.html )
  |-- js        ( index.js, module.js )
    |-- map     ( index.map, module.map )
  |-- css       ( index.css )
    |-- js      ( index.css, css-loader.js bundle-url.js )
    |-- map     ( css-loader.js, bundle-url.js )
  |-- png       ( bg.png )
로그인 후 복사

因为要对 css 热更新,所以新增了 css-loader.js, bundle-url.js 两个 js

replaceBundleNames替换引用:生成树之后将代码中的文件引用替换为最终打包的文件名,如果是生产环境会替换为 contentHash 根据内容生成 hash

hmr更新: 判断启用 hmr 并且不是第一次构建的情况,调用 hmr.emitUpdate 将改变的资源发送给浏览器

Bundle.package 打包

unloadOrphanedAssets 将独立的资源删除

package

package 将generated 写入到文件
有6种打包:
CSSPackager,HTMLPackager,SourceMapPackager,JSPackager,JSConcatPackager,RawPackager
当开启 scopeHoist 时用 JSConcatPackager 否则 JSPackager
图片等资源用 RawPackager

最终我们的例子被打包成 index.html, src.[hash].js, src.[hash].map 3个文件

index.html 里的 js 路径被替换成立最终打包的地址

我们看一下打包的 js:

parcelRequire = (function (modules, cache, entry, globalName) {
  // Save the require from previous bundle to this closure if any
  var previousRequire = typeof parcelRequire === 'function' && parcelRequire;
  var nodeRequire = typeof require === 'function' && require;

  function newRequire(name, jumped) {
    if (!cache[name]) {
      localRequire.resolve = resolve;
      localRequire.cache = {};

      var module = cache[name] = new newRequire.Module(name);

      modules[name][0].call(module.exports, localRequire, module, module.exports, this);
    }

    return cache[name].exports;

    function localRequire(x){
      return newRequire(localRequire.resolve(x));
    }

    function resolve(x){
      return modules[name][4][x] || x;
    }
  }
  for (var i = 0; i <p>可以看到代码被拼接成了对象的形式,接收参数 module, require 用来模块导入导出,实现了 commonjs 的模块加载机制,一个更加简化版:</p><pre class="brush:php;toolbar:false">parcelRequire = (function (modules, cache, entry, globalName) {
  function newRequire(id){
    if(!cache[id]){
      let module = cache[id] = { exports: {} }
      modules[id][0].call(module.exports, newRequire, module, module.exports, this);
    }
    return cache[id]
  }
  for (var i = 0; i <p>代码被拼接起来:</p><pre class="brush:php;toolbar:false">`(function(modules){
  //...newRequire
})({` +
  asset.id +
    ':[function(require,module,exports) {\n' +
        asset.generated.js +
      '\n},' +
'})'
로그인 후 복사
(function(modules){
  //...newRequire
})({
  "src/index.js":[function(require,module,exports){
    // code
  }]
})
로그인 후 복사

hmr-runtime

上面打包的 js 中还有个 hmr-runtime.js 太长被我省略了
hmr-runtime.js 创建一个 WebSocket 监听服务端消息
修改文件触发 onChange 方法,onChange 将改变的资源 buildQueue.add 加入构建队列,重新调用 bundle 方法,打包资源,并调用 emitUpdate 通知浏览器更新
当浏览器接收到服务端有新资源更新消息时
新的资源就会设置或覆盖之前的模块
modules[asset.id] = new Function('require', 'module', 'exports', asset.generated.js)
对模块进行更新:

function hmrAccept(id){
  // dispose 回调
  cached.hot._disposeCallbacks.forEach(function (cb) {
    cb(bundle.hotData);
  });

  delete bundle.cache[id]; // 删除之前缓存
  newRequire(id); // 重新此加载

  // accept 回调
  cached.hot._acceptCallbacks.forEach(function (cb) {
    cb();
  });

  // 递归父模块 进行更新
  getParents(global.parcelRequire, id).some(function (id) {
    return hmrAccept(global.parcelRequire, id);
  });
}
로그인 후 복사

至此整个打包流程结束

总结

parcle index.html
进入 cli,启动Server调用 bundle,初始化配置(Plugins, env, HMRServer, Watcher, WorkerFarm),从入口资源开始,递归编译(babel, posthtml, postcss, vue-template-compiler等),编译完设置缓存,构建 Bundle 树,进行打包
如果没有 watch 监听,结束关闭 Watcher, Worker, HMR
有 watch 监听:
文件修改,触发 onChange,将修改的资源加入构建队列,递归编译,查找缓存(这一步缓存的作用就提醒出来了),编译完设置新缓存,构建 Bundle 树,进行打包,将 change 的资源发送给浏览器,浏览器接收 hmr 更新资源

위 내용은 Parcel 소스 코드의 상세 분석(예제 포함)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

인기 기사

R.E.P.O. 에너지 결정과 그들이하는 일 (노란색 크리스탈)
3 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 최고의 그래픽 설정
3 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 아무도들을 수없는 경우 오디오를 수정하는 방법
3 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25 : Myrise에서 모든 것을 잠금 해제하는 방법
4 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

PHP와 Vue: 프런트엔드 개발 도구의 완벽한 조합 PHP와 Vue: 프런트엔드 개발 도구의 완벽한 조합 Mar 16, 2024 pm 12:09 PM

PHP와 Vue: 프론트엔드 개발 도구의 완벽한 조합 오늘날 인터넷이 빠르게 발전하는 시대에 프론트엔드 개발은 점점 더 중요해지고 있습니다. 사용자가 웹 사이트 및 애플리케이션 경험에 대한 요구 사항이 점점 더 높아짐에 따라 프런트 엔드 개발자는 보다 효율적이고 유연한 도구를 사용하여 반응형 및 대화형 인터페이스를 만들어야 합니다. 프론트엔드 개발 분야의 두 가지 중요한 기술인 PHP와 Vue.js는 함께 사용하면 완벽한 도구라고 볼 수 있습니다. 이 기사에서는 독자가 이 두 가지를 더 잘 이해하고 적용할 수 있도록 PHP와 Vue의 조합과 자세한 코드 예제를 살펴보겠습니다.

프론트엔드 면접관이 자주 묻는 질문 프론트엔드 면접관이 자주 묻는 질문 Mar 19, 2024 pm 02:24 PM

프론트엔드 개발 인터뷰에서 일반적인 질문은 HTML/CSS 기초, JavaScript 기초, 프레임워크 및 라이브러리, 프로젝트 경험, 알고리즘 및 데이터 구조, 성능 최적화, 크로스 도메인 요청, 프론트엔드 엔지니어링, 디자인 패턴, 새로운 기술 및 트렌드. 면접관 질문은 후보자의 기술적 능력, 프로젝트 경험, 업계 동향에 대한 이해를 평가하기 위해 고안되었습니다. 따라서 지원자는 자신의 능력과 전문성을 입증할 수 있도록 해당 분야에 대한 충분한 준비를 갖추어야 합니다.

간단한 JavaScript 튜토리얼: HTTP 상태 코드를 얻는 방법 간단한 JavaScript 튜토리얼: HTTP 상태 코드를 얻는 방법 Jan 05, 2024 pm 06:08 PM

JavaScript 튜토리얼: HTTP 상태 코드를 얻는 방법, 특정 코드 예제가 필요합니다. 서문: 웹 개발에서는 서버와의 데이터 상호 작용이 종종 포함됩니다. 서버와 통신할 때 반환된 HTTP 상태 코드를 가져와서 작업의 성공 여부를 확인하고 다양한 상태 코드에 따라 해당 처리를 수행해야 하는 경우가 많습니다. 이 기사에서는 JavaScript를 사용하여 HTTP 상태 코드를 얻는 방법과 몇 가지 실용적인 코드 예제를 제공합니다. XMLHttpRequest 사용

Django는 프론트엔드인가요, 백엔드인가요? 확인 해봐! Django는 프론트엔드인가요, 백엔드인가요? 확인 해봐! Jan 19, 2024 am 08:37 AM

Django는 빠른 개발과 깔끔한 ​​방법을 강조하는 Python으로 작성된 웹 애플리케이션 프레임워크입니다. Django는 웹 프레임워크이지만 Django가 프런트엔드인지 백엔드인지에 대한 질문에 답하려면 프런트엔드와 백엔드의 개념에 대한 깊은 이해가 필요합니다. 프론트엔드는 사용자가 직접 상호작용하는 인터페이스를 의미하고, 백엔드는 HTTP 프로토콜을 통해 데이터와 상호작용하는 서버측 프로그램을 의미합니다. 프론트엔드와 백엔드가 분리되면 프론트엔드와 백엔드 프로그램을 독립적으로 개발하여 각각 비즈니스 로직과 인터랙티브 효과, 데이터 교환을 구현할 수 있습니다.

PHP 코드의 소스 코드를 해석 및 실행하지 않고 브라우저에 표시하는 방법은 무엇입니까? PHP 코드의 소스 코드를 해석 및 실행하지 않고 브라우저에 표시하는 방법은 무엇입니까? Mar 11, 2024 am 10:54 AM

PHP 코드의 소스 코드를 해석 및 실행하지 않고 브라우저에 표시하는 방법은 무엇입니까? PHP는 동적 웹 페이지를 개발하는 데 일반적으로 사용되는 서버 측 스크립팅 언어입니다. 서버에서 PHP 파일이 요청되면 서버는 그 안에 있는 PHP 코드를 해석하고 실행한 후 최종 HTML 콘텐츠를 브라우저에 보내 표시합니다. 그러나 때때로 PHP 파일의 소스 코드를 실행하는 대신 브라우저에 직접 표시하고 싶을 때가 있습니다. 이 기사에서는 PHP 코드의 소스 코드를 해석 및 실행하지 않고 브라우저에 표시하는 방법을 소개합니다. PHP에서는 다음을 사용할 수 있습니다.

Go 언어 프런트엔드 기술 탐색: 프런트엔드 개발을 위한 새로운 비전 Go 언어 프런트엔드 기술 탐색: 프런트엔드 개발을 위한 새로운 비전 Mar 28, 2024 pm 01:06 PM

빠르고 효율적인 프로그래밍 언어인 Go 언어는 백엔드 개발 분야에서 널리 사용됩니다. 그러나 Go 언어를 프런트엔드 개발과 연관시키는 사람은 거의 없습니다. 실제로 프런트엔드 개발에 Go 언어를 사용하면 효율성이 향상될 뿐만 아니라 개발자에게 새로운 지평을 열어줄 수도 있습니다. 이 기사에서는 프런트엔드 개발에 Go 언어를 사용할 수 있는 가능성을 살펴보고 독자가 이 영역을 더 잘 이해할 수 있도록 구체적인 코드 예제를 제공합니다. 전통적인 프런트엔드 개발에서는 사용자 인터페이스를 구축하기 위해 JavaScript, HTML, CSS를 사용하는 경우가 많습니다.

소스 코드를 온라인으로 볼 수 있는 웹사이트 소스 코드를 온라인으로 볼 수 있는 웹사이트 Jan 10, 2024 pm 03:31 PM

브라우저의 개발자 도구를 사용하여 웹사이트의 소스 코드를 볼 수 있습니다. Google Chrome 브라우저에서: 1. Chrome 브라우저를 열고 소스 코드를 보려는 웹사이트를 방문합니다. 2. 웹의 아무 곳이나 마우스 오른쪽 버튼으로 클릭합니다. 페이지에서 "검사"를 선택하거나 단축키 Ctrl + Shift + I를 눌러 개발자 도구를 엽니다. 3. 개발자 도구의 상단 메뉴 표시줄에서 "요소" 탭을 선택합니다. 4. HTML 및 CSS 코드를 확인합니다. 웹사이트의.

Golang과 프런트엔드 기술의 결합: Golang이 프런트엔드 분야에서 어떤 역할을 하는지 살펴보세요. Golang과 프런트엔드 기술의 결합: Golang이 프런트엔드 분야에서 어떤 역할을 하는지 살펴보세요. Mar 19, 2024 pm 06:15 PM

Golang과 프런트엔드 기술의 결합: Golang이 프런트엔드 분야에서 어떤 역할을 하는지 살펴보려면 구체적인 코드 예제가 필요합니다. 인터넷과 모바일 애플리케이션의 급속한 발전으로 인해 프런트엔드 기술이 점점 더 중요해지고 있습니다. 이 분야에서는 강력한 백엔드 프로그래밍 언어인 Golang도 중요한 역할을 할 수 있습니다. 이 기사에서는 Golang이 프런트엔드 기술과 어떻게 결합되는지 살펴보고 특정 코드 예제를 통해 프런트엔드 분야에서의 잠재력을 보여줍니다. 프론트엔드 분야에서 Golang의 역할은 효율적이고 간결하며 배우기 쉬운 것입니다.

See all articles