GVKun编程网logo

Webpack 配置详解及实现过程(webpack简单配置)

18

在本文中,我们将详细介绍前端每周清单:React许可问题进展,ShopifyWebVR购物,原生JS&n的各个方面,并为您提供关于react许可证问题的相关解答,同时,我们也将为您带来关于前端每周清单

在本文中,我们将详细介绍前端每周清单:React 许可问题进展,Shopify WebVR 购物,原生 JS&n的各个方面,并为您提供关于react许可证问题的相关解答,同时,我们也将为您带来关于前端每周清单: Node与Rust、值得使用的React组件库、前端每周清单加量版:Vue 3.0展望、React代码迁移、前端每周清单半年盘点之 React 与 ReactNative 篇、前端每周清单半年盘点之 WebAssembly 篇的有用知识。

本文目录一览:

前端每周清单:React 许可问题进展,Shopify WebVR 购物,原生 JS&n(react许可证问题)

前端每周清单:React 许可问题进展,Shopify WebVR 购物,原生 JS&n(react许可证问题)

前端每周清单: Node与Rust、值得使用的React组件库

前端每周清单: Node与Rust、值得使用的React组件库

前端每周清单加量版:Vue 3.0展望、React代码迁移

前端每周清单加量版:Vue 3.0展望、React代码迁移

前端每周清单半年盘点之 React 与 ReactNative 篇

前端每周清单半年盘点之 React 与 ReactNative 篇

前端每周清单半年盘点之 React 与 ReactNative 篇

前端每周清单专注前端领域内容,以对外文资料的搜集为主,帮助开发者了解一周前端热点;分为新闻热点、开发教程、工程实践、深度阅读、开源项目、巅峰人生等栏目。欢迎关注【前端之巅】微信公众号(ID:frontshow),及时获取前端每周清单;本文则是对于半年来发布的前端每周清单中的 React 相关的教程实践与开源项目的盘点,可以查看这里获得往期清单或者其他盘点篇。

教程实践

  • Twitter 宣布移动 Web 技术栈迁移到 Node.js,Express,React PWA:近日,Twitter 工程师 Nicolas 宣布 Twitter 几乎所有的移动流量迁移到了以 Node.js 为基础的服务中(Today we moved all of Twitter''s mobile web traffic (that''s like, a lot) to our new web stack – Node.js, Express, React PWA.)。在过去的两年中,Twitter 移动 Web 技术栈主要是基于 Scala,Google Closure Templates 以及少量的 JavaScript。后来 CharlieCroom 开始尝试将登出服务迁移到 JavaScript 技术栈中,并且进行了约 9 个月的线上测试,效果尚可,因此 Twitter 决定全部迁移到 JavaScript 技术栈中。同时,Twitter Web APP 还支持所谓的 PRPL 范式:主动推送首屏关键资源、仅渲染初始路由、预存其他路由、按需懒加载与创建剩余路由。

  • 来自 MuseFinder 的 React 组件编写实践:该指南来源于 MuseFind 在多年的产品开发中总结而来的 React 实践经验,其包含了对于组件声明方式、样式类的使用、初始状态声明、Props 声明、方法声明、Props 结构、装饰器的使用、函数式组件的声明等等多个方面。

  • 基于 React 与 Redux Sagas 的权限验证应用开发教程:此文中作者深入地介绍了如何利用 Redux、Redux Saga、Redux Form、React Router 这些工具开发常见的权限验证应用。单独地使用某个工具似乎没啥难度,但是在工程应用中将它们较好地组合在一起却不是件容易事。而本文则是作者从自身工程实践的角度进行了介绍。

  • 基于 ReactNative 与 MobX 的 Imgur 应用开发教程:此文中作者结合 ReactNative 与 MobX 开发一个展示 Imgur 中图片的应用,涉及到了如何使用 MobX 进行状态管理、如何与 RESTful API 进行交互、如何在 ListView 中渲染全屏图片以及如何监听设备状态等。

  • 在 React 中构建微交互动画:微交互能够更好地引导用户,提升用户体验,而文本则是基于 CSS Transitions、react-motion、react-animations 构建可交互的搜索框。

  • 2017 年 React 与 Redux 学习建议: 此文是作者数年来学习与使用 React 以及 Redux 的感悟,不一定适合纯初学者,不过对于有一定基础概念的很推荐一看。

  • Twitter Redux Store 探秘:复杂应用的 Store 设计一直是开发中的难点,而作为大型内容社交软件 Twitter 之前宣布 Web 移动端逐步迁移到 Node.js、Express、React PWA 架构,本文就是对于 Twitter 的 Redux Store 设计分析与探秘。

  • React Native 与 Swift 性能对比:作为混合式开发框架,React Native 在运行时仍然会实际调用 Objective-C 或者 Java。此文作者同时用 Swift 与 React Native 构建了相同的应用,并且从 CPU、GPU、内存使用、电池耗费等多个角度对这二者进行性能分析。结果表明二者性能相差无几,Swift 在 CPU 占用略占优势,二者的 GPU 占用不相伯仲,而 React Native 在内存上则有一定长处。( http://suo.im/2MWZnA )

  • React 与 MobX 开发中的测试驱动开发: 本文对于 React 与 MobX 的基本使用进行了介绍,阐述了为何作者认为 MobX 是个不错的 Redux 的替代以及如何对 MobX 进行单元测试。( http://suo.im/2PE2A6 )

  • 基于 React 与 GraphQL 的全栈开发指南:GraphQL 最早由 Facebook 提出以解决复杂多变的查询问题,弥补 REST 中的不足。它允许界面组件以声明式获取数据而忽略后端实现细节。本系列文章是由 Apollo 团队提供,讲解如何基于 React 与 GraphQL 开发应用。( http://6me.us/O6p )

  • React 开发中的 10 个微模式:此文是 Gilbertson 在工作中总结而来的 React 开发中常见的设计模式总结,譬如输入域的唯一标识分配、CSS 控制等等 。 ( http://suo.im/42S8Kb )

  • Airbnb 使用 React 重构搜索功能的实践:早在 2015 年,Airbnb 的工程团队就决定将 React 作为主要的前端开发栈,不过因为其搜索页面过于复杂因此直到 2016 年初才开始迁移工作。本文就是 Airbnb 进行代码重构的经验介绍。( http://6me.us/2mS )

  • React Native 中的 FlatList 组件:3 月 1 日开始 ReactNative 中的 FlatList 正式从测试包中移动至正式包中;我们在项目开发中可以使用 FlatList、SectionList、VirtualizedList 来替代传统的即将被移除的 ListView。( http://6me.us/dqiO1 )

  • ReactNative 性能优化实践:日前有人表示 React Native 在 Android 上表现不佳,本文则是作者对于潜在的性能问题提出的优化方案。作者首先分析了常见的 Overdraw 问题以及可能的问题源与解决方案,然后介绍了列表中常见的 GPU 渲染瓶颈以及解决方案。( http://6me.us/qX63f )

  • React 中 setState 的函数式用法:React 生态圈中一直崇尚所谓函数式编程理念,而本文作者介绍了如何利用 setState 函数的回调来实现 setState 的函数式用法;就像 Redux 中的 reducer 一样,能够独立声明于组件外,然后声明式的使用,从而保证组件更新逻辑的清晰与可测试性。

  • 我理解的“大前端”或“大无线”:本文主要是介绍作者所在团队最近的一些变化和思考,包括前言、NodeJS职能变化、ReactNative的大规模应用、专门的架构组职能、总结五部分。。( http://6me.us/Md2 )

  • ReactRouter-V4 构建之道与源码分析:本文介绍了 React Router V4 的设计思想,一步一步由浅入深地介绍如何从零开始构建一个类似于 React Router V4 这样的秉持路由即组件的思想的路由框架。( http://6me.us/jfUwEw )

  • 来自 Formidable 的 2017 React Naive 技术栈:本文是来自 Formidable 的工程师 Jani Eväkallio 介绍的他们在 2017 选定的 React Native 开发技术栈,包括构建工具、组件库、状态管理等等方面。( http://6me.us/yH2yE )

  • Sketch:React Native 的 Playground :随着 Create React Native App 的发布,Expo 发布了能够在线编辑 React Native 应用的工具 Sketch。开发者可以在 Web 上直接编辑 React Native 应用代码,或者拖拽方式加入组件,然后通过 Expo 客户端完成本地预览。( http://6me.us/aGFX )

  • 以组件为中心的 React 懒加载:React Loadable 是以组件为中心的懒加载框架,其基于 Webpack 2 提供的 import 提供的异步代码分割与加载功能进行了一系列的封装。( http://6me.us/mNHi )

  • 来自 Vixlet 的 React 优化策略:在过去的数年中,来自 Vixlet 的前端开发团队一直使用 React 与 Redux 的开发架构,本文即是该团队分享其在开发过程中发现的 React 优化策略的介绍。( http://6me.us/dx5 )

  • Preact 内部原理探秘:Preact 是提供了类似于 React API 不过速度更快、包体更小的 React 替代包,本系列文章是 Preact 的开发者介绍其内部工作原理 。( https://parg.co/bOj )

  • CSS Grid 典型案例:该网站提供了一系列基于 React 编写的 CSS Grid 布局的测试样例,是个不错的从实例中学习 CSS Grid 语法与使用的教程。( https://sii.im/playground/css... )

  • 开发 React Native 与 Redux 应用一年来的错误总结:本文作者总结了他在过去一年中 React Native 与 Redux 开发中遇到的错误的复盘与总结,譬如布局文件分割、Redux Store 设计、项目目录结构、表单验证等多个方面。( https://parg.co/bQS )

  • React Conf 2017 盘点:本文作者对于近日举办的 React Conf 2017 中的精彩演讲进行了盘点,包括 Redux 与 MobX 在状态管理领域的对比、ReactVR 等一系列优秀的基于 React 的扩展项目、代码格式化与样式组件、服务端渲染等等。( https://parg.co/bsg )

  • Redux 实践大讨论:此篇是 Markerikson 在 Redux Issue 中发起的讨论,主要涉及 Redux 模板冗余、过度抽象、学习曲线过于曲折、太多的 Opinioned 最佳实践等问题。( https://github.com/reactjs/re... )

  • 2017 简明 React 入门指南:本文是针对那些熟悉 jQuery 与传统 JavaScript 开发的前端工程师准备的现代 React 开发入门指南,其包括了环境配置、create-react-app 使用、学习资料、应用编写与发布等等章节。( https://parg.co/bCx )

  • React Bits:一本关于 React 设计模式、技术与技巧的书,涵盖了常见的 React 应用开发中的设计模式、需要规避的反模式、处理 UX 变种、性能调试与样式处理等等。( https://github.com/vasanthk/r... )

  • 基于 ReactNaive 与 Uber 工程基础构建 UberEATS:本文是 UberEATS 的工程师团队介绍的他们基于 Uber 原工程架构与 ReactNative 实现应用的工程实践;包括了构建迁移路径、应用架构定义、自动更新、测试与静态类型检测等等。( https://eng.uber.com/ubereats... )

  • 微软开源跨平台开发框架 ReactXP:ReactXP 是来自于微软的用于开发跨平台(iOS,Android,Web,Windows)应用的开源框架,其基于 React.js 与 React Native 项目,提供了类似的接口与语法规则;能够帮助开发者快速创建优美、响应式的 Web 界面以及原生体验的移动应用。( https://microsoft.github.io/r... )

  • 基于 React,Redux,React-Router-V4 以及 Firebase 创建实时足球投票应用:本系列教程基于 React,Redux,Redux-Saga,React-Router V4 以及 Firebase 创建足球投票应用,在第一篇教程中主要介绍如何使用 create-react-app 脚手架来初始化项目结构并且添加必须的库。( https://parg.co/bhD )

  • Webpack 与 Rollup:求同存异:近日,Facebook 宣布将 React 的构建工具由 Webpack 迁移到 Rollup,引发了很多开发者的讨论。本文则是深度介绍 Webpack 与 Rollup 的异同,最后总结而言,Webpack 适合于构建应用,而 Rollup 适用于构建库或框架。( https://parg.co/b4y )

  • React 中的状态管理架构模式:本系列文章着眼于对于现代复杂 Web 应用,譬如 React 或类似框架,的开发中常见的状态管理的架构模式。文章中会依次介绍 Naive Hierarchical Architectural Pattern、Top-Heavy Architecture、Flux 等等内容。( https://parg.co/b4J )

  • 基于 JavaScript 构建数据表达式分词器:本文是一篇挺有意思的文章,介绍如何利用 JavaScript 解构常见数学表达式并且从中提取出相关实体。本文涉及到的内容包括对于分词器的简单介绍、对于抽象语法树 AST 的介绍以及最终如何使用代码来实现分词算法。( https://parg.co/bRO )

  • Twitter Lite 与高性能可扩展 React PWA 实践:本文是 Twitter 工程师团队介绍其在开发世界上最大的 PWA 应用之一, Twitter Lite 过程中克服各种各样的性能瓶颈的实践经验。其核心思想包括基于路由的代码切分、避免可能导致掉帧的函数、使用压缩比更好的图片资源、以及优化 React 更新过程、避免频繁修正 Redux Store、延迟注册 ServiceWorker 等部分。( https://parg.co/bRV )

  • React Native 性能优化:本文作者承接 React Native 性能瓶颈与解决方案,以新的实际开发中的例子讨论如何优化 React Native 应用性能。作者以类似于 Android 中 Toolbar 的列表为例,介绍了如何对性能进行测试、使用原生的滚动监听、使用声明式接口等多个方面的内容。( https://parg.co/bRk )

  • 后 MVC 时代:在很长一段时间里,MVC(Model-View-Controller)架构是构建应用的黄金法则,而近几年随着 React,Vue.js,Angular 等以组件为中心的库的流行,MVC 架构在前端却趋于平寂。开发者往往将模型、视图与控制器耦合在单个实体内,而打破了传统的 MVC 架构中的约束。类似于 Flux 或者响应式编程的设计思想也改变了应用状态的处理方式,不同于 MVC 中的双向绑定,而是数据在实体之间单向流动。本文即是讨论在所谓后 MVC 时代的 GUI 应用架构的思考。( https://realm.io/news/the-pos... )

  • CodeSandbox:CodeSandbox 是一个在线的 React 编辑器,其能够帮助开发者更快更方便地展示与分享基于 React 的项目。CodeSandbox 会自动化执行类似于编译、打包、依赖管理等多种项目构建中的常见任务,同时 CodeSandbox 还允许开发者添加自定义的 node_modules 中的依赖。( https://parg.co/bR8 )

  • Slate:Slate 是类似于 Draft.js 的灵活可自定义的富文本编辑器构建框架,Slate 允许你构建功能丰富的类似于 Medium、Dropbox Paper、Canvas 这样的编辑器。Slate 提供了各式各样的插件,你可以基于 React 与 Immutable 来构建自定义的插件,并且指定哪些插件属于核心插件。( https://docs.slatejs.org/ )

  • Facebook 发布 React VR 来简化 Web 中虚拟现实应用的开发:近年来,虚拟现实技术迅猛发展,有望成为下一个主流计算平台。而 Facebook 近日正式发布 React VR,其能够帮助开发者快速构建 VR 应用。React VR 同样基于 React 与 React Native 提供了声明式的代码风格,能够允许有 React 开发经验的开发者快速上手。( https://parg.co/bfR )

  • 大型高性能React PWA如何消除各类性能瓶颈?:想要构建一款性能出色的 Web 应用程序,我们需要投入大量技术周期以检测时间浪费点、了解其发生原因并尝试各类解决方案。遗憾的是,这种做法往往无法快速解决问题。性能无疑是一项永恒的命题,技术人员永远徘徊在观察与测量当中,却几乎永远找不到最优解。不过利用 Twitter Lite,我们已经在众多层面内取得了细小但却极具价值的改进:从初始加载时间到React组件渲染(防止二次渲染),再到图像加载以及更多层面。尽管大多数变更本身并不显著,但其相加所带来的最终结果是,我们得以构建起一款规模极大且速度极快的渐进式 Web 应用程序。( https://parg.co/bfM )

  • Airbnb 设计团队发布 React SketchAPP:Airbnb 设计团队近日发布能够将 React 组件渲染到 Sketch 文档中的开源工具,它为开发工程师与设计师之间提供了便捷的沟通桥梁。( http://airbnb.design/painting... )

  • 一系列优秀的 React 界面框架:本文列举了多个优秀的 React 界面框架,分析了其特性、适用场景以及潜在的缺陷。本文涉及的框架包括 Material UI、React Desktop、Semantic-UI-React、Ant-Design、Blueprint、React Bootstrap、React Toolbox、Grommet、Fabric 等等。( https://parg.co/bNh )

  • 来自 Vixlet 的 React 优化建议:近年来 Vixlet 的 Web 团队逐步将其 Web 框架迁移到了 React + Redux 技术架构,本文是来自于 Vixlet 的 React 优化实践总结与建议。( https://parg.co/bNF )

  • 从实用主义视角来看现代前端应用开发:现代 Web 开发技术变革迅速,而我也经历了从纯 JS 、jQuery、Vaadin、Angular JS、React 等等一系列的变迁。本文则首先思考何谓现代 Web 应用,然后考虑现代 Web 应用常用的项目架构与构建方式,譬如 TypeScript、Webpack、Linting 等内容,然后讨论现代常用的技术架构,譬如 React.j、MobX、依赖注入等相关知识。( http://dimafeng.com/2017/04/2... )

  • React 动画系列教程:本系列教程着眼于介绍 React 动画开发相关知识,而本文则是从 CSS transitions 基础入手,介绍了 CSS transitions 的基础语法与进度条、导航栏等经典案例。( https://parg.co/bMF )

  • 使用 React、Redux 以及 Webpack 创建 TODO 应用:本文是面向新手的教学文章,介绍了如何利用 React、Redux 以及 Webpack 创建简单的 TODO 应用,包括利用 Webpack 搭建构建环境、编写基本的 React 组件以及使用 Redux 管理应用状态等内容。( https://parg.co/bMT )

  • 函数式组件的函数式调用:本文是来自 Missive 的工程师分享了他们在基于 React 进行应用开发时的技巧,即如果直接以函数调用而非组件的方式来使用函数式组件,可以避免对于 React.createElement 的调用,最终相同组件的渲染耗时可以节约近 45%。( https://parg.co/bMa )

  • 拥抱 React Router 4,改变旧的思维习惯:在今年的 React 大会上,Michael Jackson 以及 Ryan Florence 发布了所谓“Learn Once,Route Anywhere”的演讲。同时也代表了 React Router 4 中的核心思想:路由即声明式组件;本文则介绍了 React Router V3 到 React Router V4 的变化。( https://parg.co/bVv )

  • 高性能动态 CSS 样式:本文是对 JSS 新近提供的函数式值的介绍,其与 React 内联样式以及其他 CSS 解决方案相比有数倍的性能提升。在 Web 开发中动态设置样式往往会触发页面的重渲染,而本文则是介绍了如何使用 CSSOM 的 API 来在元素渲染之前即完成样式的设置。( https://parg.co/btW )

  • React 新引擎 React Fiber 究竟要解决什么问题?:Facebook 正在以流行的 JavaScript 框架 React 为基础开发一个全新的架构。这个名为 React Fiber 的全新设计改变了检测变更的方法和时机,借此可改进浏览器端和其他渲染设备的响应速度。 这一 全新架构 最初已于 2016 年 7 月公开发布,其中蕴含着过去多年来 Facebook 不断改进的工作成果。该架构可向后兼容,彻底重写了 React 的协调(Reconciliation)算法。该过程可用于确定出现变更的具体时间,并将变更传递给渲染器。( https://parg.co/btw )

  • GUI 应用程序架构的十年变迁:MVC、MVP、MVVM、Unidirectional、Clean:随着现代浏览器的日渐流行,Web 以及混合开发技术的发展,大前端的概念日渐成为某种共识;而无论 iOS、Android、Web 这样的端开发还是 React Native、Weex 这样的跨端开发,其术不同而道相似纵览这十年内的架构模式变迁,大概可以分为 MV 与 Unidirectional 两大类,而 Clean Architecture 则是以严格的层次划分独辟蹊径。从笔者的认知来看,从 MVC 到 MVP 的变迁完成了对于 View 与 Model 的解耦合,改进了职责分配与可测试性。而从 MVP 到 MVVM,添加了 View 与 ViewModel 之间的数据绑定,使得 View 完全的无状态化。最后,整个从 MV 到 Unidirectional 的变迁即是采用了消息队列式的数据流驱动的架构,并且以 Redux 为代表的方案将原本 MV* 中碎片化的状态管理变为了统一的状态管理,保证了状态的有序性与可回溯性。( https://zhuanlan.zhihu.com/p/... )

  • 新版本 Create React App 特性概述:不到一年前,React 官方发布了 Create React App 这个零配置的快速创建 React 应用的脚手架工具;而本文则介绍了近几个月来 Create React App 中加入的新特性。新版的 Create React App 中切换到了 Webpack 2,并且优化了运行时错误提示,同时还默认启用了 Progressive Web Apps 支持,并且引入了 Jest 20、动态导入等等一系列的新特性。( https://parg.co/bkY )

  • React Native 开发中的 80/20 定律:在构建 React Native 应用时,我们常常发现某些 20% 的投入会带来 80% 的产出。本文则是作者在构建了自己首个 React Native 应用之后的感悟,作者发现引入静态类型、通用组件以及精益部署之后,整个想法的开发速度与项目质量得到了较大地提升。( https://parg.co/bko )

  • 从零开始构建 WhatsApp 应用:本系列文章深入浅出地介绍了如何利用 GraphQL 与 React Native 构建类似于 WhatsApp 的应用 Chatty。前几部分主要介绍了如何搭建基础环境、设计 GraphQL Schemas、进行数据查询与交互等内容,而本文则着重于介绍如何为 Chatty 添加权限验证特性。( https://parg.co/bk0 )

  • 如何快速地为 React 站点设置 A/B 测试:A/B 测试,或者称为分割测试,是用来随机地为用户展示网页以测试不同产品设计的反馈效果。A/B 测试对提升真实应用的用户接受度非常有帮助,而本文则是介绍了如何利用 react-ab-test 这个工具快速地针对 React 站点设置 A/B 测试收集用户反馈信息。( https://parg.co/bkE )

  • 重构 Airbnb 前端架构:本文是近日 Airbnb 开发团队在思索重构代码库中 JavaScript 部分的经验总结,主要着眼于产品驱动开发以及技术沉淀、从传统的 Rails 架构中积攒的经验以及新的技术栈的某些特性等方面。本文首先介绍了从 Rails 迁移过程中的一些经验,譬如将原本完全的服务端渲染界面所需要的数据切分为了 API 与 Non-API 两大类,并且使用 Hypernova 来进行 React 服务端渲染。然后介绍了如何在应用前端通过引入懒加载与异步加载等方式提升前端性能与用户体验。( https://parg.co/bkA )

  • React Europe 2017 见闻实录:本文记录了作者在第三届 React Europe 大会上的见闻,也是不错的窥见 React 生态圈现状与未来发展方向的方式。本文首先介绍了即将到来的 React 16 以及新的调和算法 Fiber,然后介绍了一些辅助构建高质量 JavaScript 代码的工具,最后还讨论了基于流的按帧渲染方式。( https://parg.co/bJt )

  • 理解高阶组件:即使 React 新手都应该听过所谓高阶组件或者容器组件的概念,而本文则是深入浅出地介绍了 React 中高阶组件的概念与意义,并且以实例介绍具体的使用方式与适用场景。作者首先介绍了无状态组件与全局状态的概念,然后对比了所谓容器与展示型组件的使用场景,最后介绍了常见的高阶组件。( https://parg.co/biZ )

  • 我们为什么选择使用 React 生态:本文是京东金融移动研发部工程师分享的它们对于前端框架、工具与方法的选择过程中的考虑。( https://parg.co/biP )

  • hacker-news-pwas:基于不同的前端框架实现的符合 PWA 应用特性的 Hacker News APP 的合集,包括了常见的 React、Angular、Vue、Preact 等多个版本,并且均在 Lighthouse 评测中达到 90 以上的评分。( https://parg.co/biQ )

  • 使用 Vue 与 NativeScript 开发跨端应用:目前标准的开发 NativeScript 应用的方式是使用朴素的 JavaScript 或者 Angular,而本文介绍了如何结合使用 Vue 与 NativeScript 来开发跨终端应用。本文首先阐述了 Vue.js 相较于 React 或者 Angular 的优势,然后阐述了使用 Vue 语法来开发基础 NativeScript 应用的步骤。( https://www.nativescript.org/... )

  • 利用 React Apollo 减少 Redux 代码量:Redux 为人诟病的一点就是需要大量的模板代码,而更多的代码往往也意味着更多的潜在错误与更高的维护代价。本文则介绍了如何利用 Apollo 来接管应用中的数据加载与呈现逻辑,从而减少 Redux 实现方案中加载数据生命周期中所需要的代码。( https://parg.co/bLA )

  • 九个 React Native 动画指南:本文通过介绍九个 React Native 动画地实现从零到一的介绍了 React Native 中的动画机制。包含了通过 Animated.timing 来添加样式动画、创建可伸缩的按钮、创建可拖拽的卡片、动态地变换元素的颜色、角度、序列位置等等实例。( https://parg.co/b9d )

  • 构建 React 组件库:本系列文章循序渐进地介绍如何设计编写自己的小型组件库并且将其发布到 NPM 仓库中;第一篇文章着眼于如何从零开始搭建开发环境,第二篇文章则介绍如何利用 styled-components 来为组件添加样式、添加调色板、构建高效开发流程以及如何实践 Atomic Design 原则。( https://parg.co/b9u )

  • 5 个提升 React Native 应用性能的方法:本文作者分享了自己在过去一段时间内尝试提升公司 React Native 应用性能的实践经验,包括如何设置有效的性能测试、强制启动 no-bind 规则、使用函数式组件、重制 TabMap 的逻辑等等。( https://parg.co/b93 )

  • 京东 618:如何配合业务打造 JDReact 三端融合开发平台?:良好解决多终端开发问题是提升团队开发效率的有效方法,本文全面解析了京东 JDReact 三端融合平台。本文首先回顾了传统无线开发的痛点,然后讨论了 React Native 的优势与局限,最后介绍了 JDReact 三端融合平台的整体架构、在功能、加载性能、内存方面的改进与优化以及发布到生产环境中的流程等内容。( https://parg.co/b9U )

  • React 服务端渲染:本文循序渐进地介绍了 React 中服务端渲染的相关知识,首先讨论了服务端渲染相较于客户端渲染带来的优势、然后介绍了如何在 React 中添加服务端渲染的支持,最后还讨论了如何通过同构的高阶函数在服务端抓取数据然后显示在客户端。( https://css-tricks.com/server... )

  • 大前端公共知识梳理:这些知识你都掌握了吗?:近年来,随着移动化联网浪潮的汹涌而来与浏览器性能的提升,iOS、Android、Web 等前端开发技术各领风骚,大前端的概念也日渐成为某种共识。 其中特别是 Web 开发的领域,以单页应用为代表的富客户端应用迅速流行,各种框架理念争妍斗艳,百花竞放。Web 技术的蓬勃发展也催生了一系列跨端混合开发技术,希望能够结合 Web 的开发便捷性与原生应用的高性能性;其中以 Cordova、PWA 为代表的方向致力于为 Web 应用尽可能添加原生体验,而以 NativeScript、ReactNative、Weex 为代表的利用 Web 技术或者理念开发原生应用。 平心而论,无论哪一种开发领域或者技术,他们本质上都是进行图形用户界面(GUI)应用程序的开发,面对的问题、思考的方式、架构的设计很大程度上仍然可以回溯到当年以 MFC、Swing、WPF 为主导的桌面应用程序开发时代,其术不同而道相似。( https://parg.co/byS )

  • React Express:针对目前 React 及其生态圈学习曲线过于陡峭的囧境,作者希望创建一个多合一的面向初学者的 React 技术栈学习教程,从最简单的 create-react-app、npm、webpack、babel 等工具的使用,到 ES2015、ES2016、JSX 等基础语法,最后还包括 React、Redux、CSS-in-JS 等工程实践。( https://github.com/dabbott/re... )

  • Airbnb React VR 实践:Airbnb 自 2014 年以来一直使用 React 构建用户交互界面,并且为社区贡献了很多优秀的开源项目;而随着 React VR 的发布,Airbnb 也利用其来快速原型化与测试 VR 相关的创意。本文即是介绍 Airbnb 在 React VR 实践方面的一些经验总结,本文首先阐述了 React、React Native 与 React VR 三者之间的关系与差异,然后介绍了 React VR 在布局、基础组件方面的语法,最后还讨论了 React VR、WebVR 以及 VR 技术本身的发展可能性。更多 WebVR 相关资料参考 https://parg.co/bFR。

  • 深入 React 动画实践:本文介绍了在 React 开发中多种创建动画效果的途径,包括了基于 React 组件状态的 CSS 动画、基于 React 组件状态的 JavaScript 样式动画以及第三方依赖的 React Motion、Animated、Velocity-React 等库。本文最后还讨论了如何用 GreenSock 等经典强大的动画库来辅助 React 组件动画开发;更多 React 相关资料参考 https://parg.co/bM1 。

开源项目

  • metro-bundler:为了更好地社区支持,原 react-native-packager 被独立为 Metro Bundler;其致力于打造具有亚秒级别的重载以及较好可扩展性的模块系统,同时它仍然是 React Naive 内置的开箱即用的工具。( https://github.com/facebook/m... )

  • React Flight: React Flight 能够帮我们轻松地构建组件之间的过渡动画,它允许开发者定义初始状态的组件与结束状态的组件,React Flight 会自动地完成组件之间的切换并且添加动画效果。

  • React Native Node: React Native Node 能够在基于 React Native 开发的 Android 应用中启动后台 Node.js 进程,从而可以利用 Node.js 中的流、文件系统接口等特性来进行功能操作;React Native Node 主要依靠 Node.js 7.1.0 版本能够被独立编译为 bin_node_v710 可执行文件。另一方面,尽管 iOS 并不支持直接运行 V8,但是该项目正在致力于为 ChakraCore 打造类 V8 特性支持。

  • react-simple-maps: react-simple-maps 是基于 d3-geo 与 topojson 的 React 地图组件库,允许开发者快捷方便地构建自定义的 SVG 地图;目前的特性包括了缩放、标记、自定义 SVG 标记、自定义着色、气泡图、动画支持等等。

  • redux-query:React/Redux 中查询与管理网络状态的库:对于很多开发者而言,同步本地状态与网络状态会是一件很麻烦的事情。其中需要太多的妥协与考量,甚至于面对不同的问题需要使用不同的技术栈。而 redux-query 即是 AmplitudeEng 的工程师在实践中的总结与应用,它可以被当做基于 React/Redux 以及 RESTful API 的应用的很好的辅助工具。它允许将网络状态链入到当前的 Redux Store 中,并且提供了删除、乐观更新、响应缓存、删除重复等等优秀的功能。

  • react-native-offline-utils:react-native-offline-utils 允许我们在 React Native 应用中优雅地处理离线情况,能够根据连接情况动态判断需要使用的组件渲染或者数据抓取逻辑。同时 react-native-offline-utils 还能够平滑地集成 Redux,能够自动转发特殊的离线 Action。( https://github.com/rauliyohmc... )

  • react-pdf:在浏览器、移动端与服务端皆可适用的基于 React 语法的 PDF 文件创建工具。( https://github.com/diegomura/... )

  • Rapscallion:React 服务端渲染的性能一直是广为诟病,相较于其他前端框架会满上很多,笔者在此文中也进行过简要探讨。而 Rapscallion 则是新的支持 React 服务端渲染的开源包体,它支持异步非阻塞渲染,相较于renderToString其能达到将近 50% 的性能提升。同时 Rapscallion 官方还为我们准备了基于 Redis 的缓存实例。( http://suo.im/3YS6pz )

  • react-native-interactable:react-native-interactable 是由 wix 发布的用于创建高性能用户交互效果的声明式接口。典型的用户场景包括滑动式卡片、抽屉菜单、伸缩式应用头、聊天头等。( https://github.com/wix/react-... )

  • react-overdrive:非常简单易用的 React 应用转场动画实现库,能够在不同的页面间指定相同 ID 的元素,Overdrive 会自动为这两个元素之间添加转场动画。( https://github.com/berzniz/re... )

  • react-perimeter:react-perimeter 能够为目标元素创建隐藏的栅栏,当用户的鼠标移动到目标元素的指定范围内时会触发预设时间,譬如可以执行组件预加载等操作。( https://github.com/aweary/rea... )

  • glamorous:来自 PayPal 的 React 组件的 CSS-in-JS 解决方案,其支持 JSX 语法、自定义样式组件等多种灵活的功能。( https://parg.co/b4Q )

  • React Data Grid:基于 React 构建的类似于 Excel 的网格组件,其提供了编辑、键盘导航、复制粘贴等多种功能。( https://github.com/adazzle/re... )

  • create-next-app:基于 Next.js 的类似于 create-react-app 的快速创建支持服务端渲染的 React 应用的命令行辅助工具。( https://open.segment.com/crea... )

  • Create XP App: 近日,微软的 Skype 团队发布了基于 React Native 的跨平台开发框架 ReactXP,而 create-xp-app 则是快速创建 ReactXP 应用的脚手架。本文则是对于 create-xp-app 的安装与基本使用的介绍,包括了如何运行在 Web 与 iOS/Android 等原生环境中,以及如何进行打包等内容。

  • haul:Haul 是基于 Webpack 这样开源框架构建的 react-native 命令行工具的替代品,它支持从运行于开发时服务器到打包发布至生产环境等全生命周期的功能。Haul 的最大特性在于允许开发者使用 Webpack 生态系统中各种合影的加载器与插件,并且不需要 watchman 等外部工具的辅助,还优化了错误提示信息。( https://github.com/callstack-... )

  • react-move:方便快捷地 React 组件动画库,支持 React、React Native 以及 React VR。React Move 允许开发者忽略具体的动画属性控制而完全托管于框架,同时它还提供了多个生命周期中的回调函数方便开发者进行控制。( https://github.com/tannerlins... )

延伸阅读

  • React 学习与实践资料索引

  • React 与前端工程化实践

  • 前端每周清单半年盘点之 Vue.js 篇

前端每周清单半年盘点之 WebAssembly 篇

前端每周清单半年盘点之 WebAssembly 篇

前端每周清单专注前端领域内容,以对外文资料的搜集为主,帮助开发者了解一周前端热点;分为新闻热点、开发教程、工程实践、深度阅读、开源项目、巅峰人生等栏目。欢迎关注【前端之巅】微信公众号(ID:frontshow),及时获取前端每周清单;本文则是对于半年来发布的前端每周清单中的 WebAssembly 相关的教程实践与开源项目的盘点,可以查看这里获得往期清单或者其他盘点篇。

教程实践

  • 《对比探秘 WebAssembly 性能优越之谜》: 本系列文章通过有趣的漫画介绍了 WebAssembly 的前世今生,并且与 JavaScript 就加载、解析、编译、执行等浏览进行了详细对比,从而介绍 WebAssembly 的性能缘何相较于 JavaScript 会好上很多。同时作者也强调,WebAssembly 与 JavaScript 各有所长,未来并不会存在太多的竞争,更多的是相辅相成,各司其职。( http://suo.im/3jsTUH )
  • 《浅析 WebAssembly 缘何优于 Asm.js》:WebAssembly 是新的 Web 中可执行格式,逐现代浏览器纷纷地提供了对于 WebAssembly 的原生支持;本文则是对于 WebAssembly 相较于 asm.js 带来的性能提升背后的原理进行简要介绍。(https://parg.co/bsv)
  • 《简短的 WebAssembly 卡通指南》:现在有很多关于 WebAssembly 与 JavaScript 生态圈的讨论,人们往往关注于 WebAssembly 带来的巨大的性能提升以及它会如何颠覆现代 Web 开发。不过很多的介绍中并没有详细阐述隐藏在速度提升之后的具体细节,本文则是从整个 JavaScript 的演化史来介绍 WebAssembly 巨大性能提升的原因。( https://parg.co/bVa )
  • 《理解 WebAssembly 的文件格式》:为了保证 WebAssembly 能够被人们阅读与理解,需要提供对于 wasm 二进制格式的文本表示。该特性着眼于能够在文本编辑器、浏览器开发者工具等开发工具中浏览 WebAssembly 文件,而本文则介绍了这种文件格式的规范与工作原理,以及底层的字节码与上层的 JavaScript 对象之间的关联关系。( https://parg.co/bk6 )
  • 《这 WebAssembly,是 Mozilla 赢了》:Mozilla 提出1 asm.js 与 Google Chrome 提出的 PNaCI 是都是致力于在浏览器中运行原生代码的技术方案。不过 PNaCI 却存在着自绝于 JavaScript 以及 HTML 等问题,并且其他的浏览器厂商很难去支持 PNaCI 标准。而 asm.js 则以轻量级的对于标准 Web 平台扩展的方式实现了这一目标,也就导致了最终 WebAssembly 决定靠近 asm.js 而不是 PNaCI。( http://robert.ocallahan.org/2... )
  • 《WebAssembly 初体验:重构简单游戏引擎》:WebAssembly 为我们提供了构建高性能的前端应用的途径,而本文则从零开始介绍如何使用 C 来覆写简单的 JavaScript 游戏引擎并且将其编译为 WebAssembly。本文依次介绍了如何搭建基础的 Emscription 工具链、使用 JavaScript 引入 wasm 模块、覆写并且优化某个小型游戏引擎、两个引擎的性能评测等等部分。( http://blog.openbloc.fr/webas... )
  • 《Figma 利用 WebAssembly 降低三倍加载速度》:自 WebAssembly 推出之后,很多开发者都开始尝试在小型项目中实践 WebAssembly,不过尚缺大型真实案例比较。而 Figma 因为其产品主要基于 C++ 实现,可以方便地编译到 WebAssembly 中并且与原方案进行性能比较。本文中 Figma 介绍了它们在 Firefox 中使用 WebAssembly 之后带来的加载性能提升以及下载尺寸的优化,同时还提及了目前 WebAssembly 在实际项目使用中存在的一些问题与风险。( https://parg.co/biB )
  • Rust、WebAssembly 与 Webpack:WebAssembly 是新的运行于 Web 平台的二进制格式,我们能够将 C、C++、Rust 这些语言编译到 .wasm 文件格式中然后在浏览器环境下运行他们;通常这些编译后的代码在包体体积与运行速度上都会比 JavaScript 有明显提升。而本文则着眼于介绍如何在浏览器中执行底层的 Rust 代码,也可以参考这篇文章( https://parg.co/by4 )来了解更多的关于 WebAssembly 快速实践的知识。( https://parg.co/byh )
  • WebAssembly 在 PSPDFKIT 的实践:随着今年三月份 WebAssembly Community Group 就标准达成一致,越来越多的主流浏览器开始支持 WebAssembly,本文即是 PDF 工具开发者 PSPDFKIT 介绍它们利用 WebAssembly 开发浏览器端渲染的 PDS 预览工具的实践经验。本文首先介绍了 asm.js 的工作原理与编译机制,然后阐述了 WebAssembly 的概念与组成,最后介绍了 WebAssembly 在 PSPDFKit 的实践经验以及他们在将原本大型 C++ 代码库转化为 WebAssembly 格式时的体验;更多 WebAssembly 相关资料参考这里。

开源项目

  • asm-dom: asm-dom 是轻量级的基于 WebAssembly 的 Virtual DOM 框架,其允许开发者使用 C++ 来构建 Web 单页应用。开发者可以仅使用 C++ 来编写 Web 应用,然后通过 Emscripten 来将其转化为 WebAssembly;其能允许开发者直接使用现有的 C++ 标准库代码,从而保证代码复用与性能提升。

今天关于前端每周清单:React 许可问题进展,Shopify WebVR 购物,原生 JS&nreact许可证问题的分享就到这里,希望大家有所收获,若想了解更多关于前端每周清单: Node与Rust、值得使用的React组件库、前端每周清单加量版:Vue 3.0展望、React代码迁移、前端每周清单半年盘点之 React 与 ReactNative 篇、前端每周清单半年盘点之 WebAssembly 篇等相关知识,可以在本站进行查询。

对于想了解webpack 3 & React 的配置的读者,本文将提供新的信息,我们将详细介绍webpack react配置文件,并且为您提供关于create-react-app webpack4升级webpack5、React + webpack 环境配置、react + webpack安装配置、React系列学习笔记:1.React与webpack基本配置的有价值信息。

本文目录一览:

webpack 3 & React 的配置(webpack react配置文件)

webpack 3 & React 的配置(webpack react配置文件)

今天真是难过的一天

create-react-app webpack4升级webpack5

create-react-app webpack4升级webpack5

因为脚手架默认是隐藏webpack配置的,所以需要先运行npm run eject或yarn eject暴露配置文件,然后我们就可以开始升级了。

升级需要改动的文件包括分为package.js、 webpack.config.js、webpackDevServer.config 三处。@H_301_3@

package.json 更新

主要是webpack相关包、babel相关包、react相关包、postcss相关包,直接贴对比图了。

image.png@H_301_3@

image.png@H_301_3@

image.png@H_301_3@@H_301_3@

webpack.config.js文件的更新

部分插件弃用@H_301_3@

PnpWebpackPlugin、ManifestPlugin、WatchMissingNodeModulesPlugin、ModuleScopePlugin、typescriptFormatter,主要删除对应的配置。
​@H_301_3@

部分配置弃用@H_301_3@

output移除futureEmitAssets属性。@H_301_3@

部分配置修改@H_301_3@

output的filename修改。

image.png@H_301_3@
IgnorePlugin配置写法更新。

image.png@H_301_3@
postcss=loader写法更新,修改为下面的样子:@H_301_3@

loader: require.resolve('postcss-loader'),
        options: {
          // Necessary for external CSS imports to work
          // https://github.com/facebook/create-react-app/issues/2677
          postcssOptions: {
            ident: 'postcss',
            config: false,
            plugins:[
              'postcss-flexbugs-fixes',
              [
                'postcss-preset-env',
                {
                  autoprefixer: {
                    flexBox: 'no-2009',
                  },
                  stage: 3,
                },
              ],
              // Adds PostCSS normalize as the reset css with default options,
              // so that it honors browserslist config in package.json
              // which in turn let's users customize the target behavior as per their needs.
              'postcss-normalize',
            ],
              // Adds PostCSS normalize as the reset css with default options,
              // so that it honors browserslist config in package.json
              // which in turn let's users customize the target behavior as per their needs.
          },
         
          sourceMap: isEnvProduction && shouldUseSourceMap,
        },

​@H_301_3@

node配置移动到fallback,参考官网迁移指南。
​@H_301_3@

WorkBoxWebpackPlugin移除importWorkBoxFrom和navigateFallbackBlacklist属性。
​@H_301_3@

ForkTsCheckerWebpackPlugin 移除 formatter 属性。
[@H_301_3@

](https://stackoverflow.com/questions/65018431/webpack-5-uncaught-referenceerror-process-is-not-defined)
​@H_301_3@

部分字段更名@H_301_3@

ManifestPlugin 更名为 WebpackManifestPlugin 。
jsonpFunction 更名为 chunkLoadingGlobal 。@H_301_3@

部分报错处理@H_301_3@

报错process is not defined,解决方法:链接,这里注意一点,如果改完之后报错Cannot find module 'process/browser' ,需要安装node-libs-browser这个依赖。
​@H_301_3@

我这里最终改完的webpack.config.js 完整文件如下,这里因为项目中使用less,所以

React + webpack 环境配置

React + webpack 环境配置

安装配置Babel

  • babel-preset-es2015 ES6语法包,使代码可以随意地使用ES6的新特性。
  • babel-preset-react React语法包,专门用于React的优化,在代码中可以使用React ES6 classes的写法,同时直接支持JSX语法格式
  1. 安装Babel loader
// 安装babel-core核心模块和babel-loader
npm install babel-core babel-loader --save-dev


// 安装ES6 和 React 支持
npm install babel-preset-es2015 babel-preset-react --save-dev
  1. 配置 .babelrc

安装完Babel和它的插件,配置一下它的规则,在根目录下新建一个.babelrc空文件:

// 告诉Babel,编译JavaScript代码的时候要用这两个presets编译
 {
   "preset": ["es2015", "react”]
 }

安装配置ESLint

  1. 安装ESLint loader

为webpack添加这个preLoaders(在loader处理资源之前,先用preLoaders进行处理,代码检查在代码转换之前进行)

npm install eslint eslint-loader --save-dev

这里使用Airbnb开发配置合集eslint-config-airbnb,这个配置合集里面还包括以下3个插件:

npm install eslint-plugin-import eslint-plugin-react eslint-plugin-jsx-a11y --save-dev

npm install eslint-config-eslint
  1. 配置 .eslintrc

在根目录下新建一个.eslintrc的空文件:

{
   "extends": "airbnb",
   "rules": {
     "comma-dangle": ["error", "never"]
   }
 }

安装配置webpack

配置webpack之前,先安装一个webpack的插件——html-webpack-plugin,它可以帮助我们自动生成HTML页面,并且引入正确的JavaScript文件依赖:

npm install html-webpack-plugin —save-dev

在项根目录下新建一个webpack.config.js文件:

let path = require(''path'') 
let webpack = require(''webpack'')
let HtmlwebpackPlugin = require(''html-webpack-plugin'')
// 一些常用路径
const ROOT_PATH = path.resolve(__dirname)
const APP_PATH = path.resolve(ROOT_PATH, ''app'')
const BUILD_PATH = path.resolve(ROOT_PATH, ''build'')

module.exports = {
  entry: {
    app: path.resolve(APP_PATH, ''index.jsx'')
  },
  output: {
    path: BUILD_PATH,
    filename: ''bundle.js''
  },
  // 开启 dev source map
  devtool: ''eval-source-map'',
  // 开启 webpack dev server
  devServer: {
    historyApiFallback: true,
    hot: true,
    inline: true,
    progress: true
  },

  modules: {
    // 配置preLoaders, 将eslint 添加进去
    preLoaders: [
      {
        test: /\.jsx?$/,
        loaders: [''eslint''],
        include: APP_PATH
      }
    ],
    
    // 配置loader,将Babel添加进去
    loaders: [
      {
        test: /\.jsx?$/,
        loaders: [''babel''],
        include: APP_PATH
      }
    ]
  },

  // 配置 plugin
  plugins: [
    new HtmlwebpackPlugin({
      title: ''my first react webpack''
    })
  ],
  resolve: {
    extensions: ['''', ''.js'', ''.jsx'']
    // 在js中import加载jsx扩展名的脚本
  }
}

添加组件热加载(HMR)功能

npm install babel-preset-react-hrme --save-dev

这个preset里面其实包括两方面:

  • react-transform-hmr用来实现热加载
  • react-transform-catch-errors用来捕获render里面的方法,并且直接展示在界面上

配置一下 .babelrc:

{
  "preset": ["es2015", "react"],
  "env": {
    "development": {
      "presets": ["react-hrme"]
    }
  }
}

react + webpack安装配置

react + webpack安装配置

使用CDN库方式

  • <script src="http://static.runoob.com/assets/react/react-0.14.7/build/react.min.js"></script&gt;
  • <script src="http://static.runoob.com/assets/react/react-0.14.7/build/react-dom.min.js"></script&gt;
  • <script src="http://static.runoob.com/assets/react/browser.min.js"></script&gt;
  1. react.min.js React核心库
  2. react-dom.min.js 提供DOM相关的功能
  3. browser.min.js 用于将JSX语法转为javascript语法
<!DOCTYPE html>
	<html lang="en">
	<head>
		<Meta charset="UTF-8">
		<title>Document</title>
		<script src="http://static.runoob.com/assets/react/react-0.14.7/build/react.min.js"></script>
	    <script src="http://static.runoob.com/assets/react/react-0.14.7/build/react-dom.min.js"></script>
	    <script src="http://static.runoob.com/assets/react/browser.min.js"></script>
	</head>
	<body>
		<div id="app"></div>
		<script type="text/babel">
			ReactDOM.render(
				<h1>hello world</h1>,document.getElementById('app')
			);
		</script>
	</body>
	</html>

使用脚手架方式

我们使用webpack作为脚手架

首先新建目录test,进入test目录

安装webpack

  • npm init
  • npm install
  • npm install webpack
  • npm install webpack-dev-server --save-dev

安装react

  • npm install react --save
  • npm install react-dom --save

安装一些babel插件

  • npm install babel
  • npm install babel-core
  • npm install babel-loader
  • npm install babel-preset-react
  • npm install babel-preset-es2015

创建一些必须的文件

  • touch index.html
  • touch App.jsx
  • touch main.js
  • touch webpack.config.js

配置webpack设置编译器、服务器、载入器

var path = require('path')
var webpack = require('webpack')

module.exports = {
   entry: './main.js',output: {
      path:'./',filename: 'index.js',},devServer: {
      inline: true,port: 7777
   },module: {
      loaders: [ {
         test: /\.jsx?$/,exclude: /node_modules/,loader: 'babel-loader',query: {
            presets: ['es2015','react']
         }
      }]
   },plugins: [
      new webpack.DefinePlugin({
        'process.env': {
          NODE_ENV: JSON.stringify('production')
        }
      }),new webpack.optimize.UglifyJsPlugin()
  ]

}

根目录新建index.html

<!DOCTYPE html>
<html>
   <head>
      <Meta charset="UTF-8">
      <title>hello</title>
   </head>
   <body>
      <div id="app"></div>
      <script src="index.js"></script>
   </body>
</html>

根目录新建mian.js

import React from 'react';
import ReactDOM from 'react-dom';

import App from './App.jsx';

ReactDOM.render(<App />,document.getElementById('app'))

根目录新建App.jsx

import React from 'react';

class App extends React.Component {
   render() {
      return (
         <div>
            Hello World!!!<br />
            你好世界!!!
         </div>
      );
   }
}

export default App;

配置服务

打开package.json 找到scripts节点,插入

"scripts": {
    "start": "webpack-dev-server --hot","build": "webpack --progress --hide-modules"
 },

启动服务

npm start

React系列学习笔记:1.React与webpack基本配置

React系列学习笔记:1.React与webpack基本配置

http://www.jianshu.com/p/eb2da4fe7f5b

字数625阅读472评论0喜欢1

前言

webpack和react搭配开发非常棒可以说是最佳的搭档了,包在安装过程中开发用的使用npm install --save-dev <name>安装,生产环境需要的包用npm install --save <name>安装,国内由于网络环境建设使用淘宝的镜像源cnpm,本节使用的包列表:

  • babel一系列编译转换工具:
    babel-core babel-cli babel-loader babel-preset-es2015 babel-preset-react
  • React库:react react-dom

目录结构

|-React-redux-example
    |-node_modules/                    #包文件
    |-public/                        #静态目录
        |-assets/                    #静态资源生成目录
        |-index.html
    |-src/                            #开发目录
        |-app.js                    #client入口文件
    |-config/                        #配置目录
        |-webpack.dev.js            #webpack开发配置文件
    |-package.json        
    |-README.md
    |-.gitignore                    #git的忽略列表

config/webpack.dev.js

var path = require('path') //配置文件是放在config的目录下的,所有这里定义了一个项目的根目录变量 var projectRootPath = path.resolve(__dirname,'..') var config = { entry: path.resolve(projectRootPath,152);">'src/app.js'),output:{ path: path.resolve(projectRootPath,152);">'public',152);">'assets'),filename: 'bundle.js',publicPath: '/assets/' },module:{ loaders:[ { test:/\.js$/,exclude:/node_modules/,loader:'babel-loader',query:{ presets:['react',152);">'es2015'] } } ] } } module.exports=config;module.exports=config;

entry:要打包文件
output:打包文件位置
module:打包要加载的模块--presets用来解析ES6,React,ES7语法
publicPath:指定公共URL地址在浏览器输出文件的引用

package.json

scripts添加webpack的编译命令,由于我的webpack配置文件,放在config目录中,所以在编译时要指定--config ./config/webpack.dev.js

"scripts": {
    "build": "webpack --verbose --color --display-error-details --config ./config/webpack.dev.js "
  }

src/app.js

这里我们用最少的代码测试一下我们的webpack配置有没有问题

import React from 'react' import ReactDOM 'react-dom' ReactDOM.render(<h1>Hello</h1>,document.getElementById('app'))'app'))

public/index.html

因为没有启动web服务器,所以<script src="./assets/bundle.js"></script>用的是文件的相对地址

<!DOCTYPE html>
<html lang="en">
<head>
  <Meta charset="UTF-8">
  <title>React-redux-example</title>
</head>
<body>
  <div id="app"></div>
</body>
<script src="./assets/bundle.js"></script>
</html>

build

完成上面所有步骤后,npm run build会看到在public/assets/目录中生成了一个bundle.js文件,在浏览器中打开index.html看到Hello,我们的webpack基本配置就完成了,如图


NEXT

webpack与express开发服务器的配置,hotreplace(热替换)的配置

我们今天的关于webpack 3 & React 的配置webpack react配置文件的分享已经告一段落,感谢您的关注,如果您想了解更多关于create-react-app webpack4升级webpack5、React + webpack 环境配置、react + webpack安装配置、React系列学习笔记:1.React与webpack基本配置的相关信息,请在本站查询。

本文将分享关于在reactjs项目中如何用webpack配置组件按需加载的详细内容,并且还将对react 配置webpack进行详尽解释,此外,我们还将为大家带来关于create-react-app脚手架创建react项目,暴露webpack配置文件,如何引入less支持+antd按需加载+自定义主题、React Router 4.0 + webpack 实现组件按需加载、react webpack 按需加载初试、react webpack配置 paths.js的相关知识,希望对你有所帮助。

本文目录一览:

关于在reactjs项目中如何用webpack配置组件按需加载(react 配置webpack)

关于在reactjs项目中如何用webpack配置组件按需加载(react 配置webpack)

在使用

import {Button} from 'antd'

的时候

打开控制台,会出现这样的警告

You are using a whole package of antd,please use https://www.npmjs.com/package/babel-plugin-import to reduce app bundle size.

实际我们在用antd的时候,只需要一个Button组件,它给完全加载进项目了,要知道antd 8W多行代码,执行完之后得花一些时间吧.

于是官方就提供了下面的解决方案:

第一:

import Button from 'antd/lib/button';
import 'antd/lib/button/style'; // 或者 antd/lib/button/style/css 加载 css 文件

按需引入组件,我反正不喜欢这种,要写的代码有点多.

第二:

一劳永逸.配置babel-loader

在webpack中自行配置:

{
        test: /\.js|jsx$/,exclude: /(node_modules|bower_components)/,loader: 'babel-loader',query: {
          presets: ['es2015','react'],plugins: [["import",{ libraryName: "antd",style: "css"}]]
        }
      },

只需按照我的配置形式就可以了.

以上是我的解决方案,欢迎纠错.

create-react-app脚手架创建react项目,暴露webpack配置文件,如何引入less支持+antd按需加载+自定义主题

create-react-app脚手架创建react项目,暴露webpack配置文件,如何引入less支持+antd按需加载+自定义主题

使用 create-react-app 脚手架创建项目后,默认是不支持 less 的。所以我们需要手动添加。

第一步 暴露webpack配置文件

使用 create-react-app 创建的项目,默认情况下是看不到 webpack 相关的配置文件,我们需要给它暴露出来,使用下面命令即可

yarn eject

运行之后,我们发现多了一个config文件夹,这样就可以修改 webpack 相关配置了。

第二步 添加less 在项目根目录 使用 npm 或者 yarn 来安装antd less 和 less-loader

  1. yarn add babel-plugin-import
  2. yarn add antd
  3. yarn add less less-loader

 

第三步 修改package.json:添加antd库的样式

"babel": {
    "presets": [
      "react-app"
    ],
    "plugins": [
      [
        "import",
        {
          "libraryName": "antd",
          "style": "css"
        }
      ]
    ]
  }

第四步 复制代码修改配置环境(webpack.config.js) 定义全局变量

// style files regexes
const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;

const lessRegex = /\.less$/;
const lessModuleRegex = /\.module\.less$/;

const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;


第五步 复制代码配置less-loader

//在大概466行会看到如下代码
      {
                test: sassModuleRegex,
                use: getStyleLoaders(
                  {
                    importLoaders: 2,
                    sourceMap: isEnvProduction && shouldUseSourceMap,
                    modules: true,
                   getLocalIdent: getCSSModuleLocalIdent,
                 },
                ''sass-loader''
               ),
            },
 
 
 //在此代码后添加如下代码
 
 {
               test: lessRegex,
               exclude: lessModuleRegex,
               use: getStyleLoaders(
                 {
                   importLoaders: 2
                 },
                 ''less-loader''
               ),
            },

             {
               test: lessModuleRegex,
               use: getStyleLoaders(
                 {
                   importLoaders: 2,
                   modules: true,
                   getLocalIdent: getCSSModuleLocalIdent,
                 },
                 ''less-loader''
               ),
            },

 

第六步 复制代码定义全局样式

//注释掉大概114行

// if (preProcessor) {
    //   loaders.push({
    //     loader: require.resolve(preProcessor),
    //     options: {
    //       sourceMap: isEnvProduction && shouldUseSourceMap,
    //     },
    //   });
    // }
    // return loaders;

//替换为如下
    if (preProcessor) {
      let loader = require.resolve(preProcessor)
      if (preProcessor === "less-loader") {
        loader = {
          loader,
          options: {
            modifyVars: { //自定义主题
              ''primary-color'': '' #1890ff '',
            },
            javascriptEnabled: true,
          }
        }
      }
      loaders.push(loader);
    }
    return loaders;

 

第七步 复制代码修改package.json

"babel": {
    "presets": [
      "react-app"
    ],
    "plugins": [
      [
        "import",
        {
          "libraryName": "antd",
          "style": true  //修改处
        }
      ]
    ]
  }

大概执行完以上代码之后,及支持less,同时也支持antd按需加载。

其实个人觉得没必要暴露webpack,当然我个人不了解webpack,我不是专业做前端的,因为公司用到这个,所以学习一下,

我觉得antd里使用文件的方式支持按需加载,更加简单方便。直接按照高级配置一步一步做就好了。也很方便。webpack太麻烦了,

哈哈,回头也要补充一下webpack方面的知识。

React Router 4.0 + webpack 实现组件按需加载

React Router 4.0 + webpack 实现组件按需加载

  网上关于React Router 4.0的按需加载文章有很多,大致的思路都一样,但是其实具体实现起来却要根据自己的实际情况来定,这里主要介绍一下我的实现方式。

  主要方式是通过Route组件的render方法加载一个空的组件作为中间,通过空的组件用来加载具体的页面js文件,然后这个组件的内部加载完成的时候就使用webpack 的 import方法动态请求js,当js请求成功后,这个空组件显示具体的加载js内容,说起来比较晦涩,直接上代码。

  1、先看看中间组件,(由于我这里使用了Typescript, 代码里的ts代码不感兴趣的可以直接忽略即可)

  

import * as React from ''react'';
export namespace LoadComponentAsync {
    export interface Props {
        componentName: string
    }

    export interface State {
        Component: React.ReactType
    }
}

export class LoadComponentAsync extends React.Component<LoadComponentAsync.Props, LoadComponentAsync.State> {
    constructor(props) {
        super(props)
        this.state = {
            Component: null
        }
    }

    componentDidMount() {
    // 这里使用的import进行动态加载组件 import(`..
/componentPublicPath/${this.props.componentName}` /* webpackChunkName: "[request]" */ ).then(Component => { this.setState({ Component: Component.default }) }) } render () { let Component = this.state.Component if (Component) { return <Component /> } else { return null } } }

   是的,就是这么简单的一个空组件。

  2、Router部分怎么使用这个组件呢?

<Switch>
      <Route path=''/some/path''} exact render={() => {
          return <LoadComponentAsync key={''someKey''} componentName={yourComponentName}/>
      }}/>
    <Route path=''/some/path2''} exact render={() => {
          return <LoadComponentAsync key={''someKey2''} componentName={yourComponentName2}/>
      }}/>  
</Switch>

  是的还是这么的超级简单。

   3、具体的思路上面的已经是核心代码了,

  你可能还需要配置一下东西,默认的情况你每次加载对应的路由请求的js可能是0.js 1.js 2.js这个样子的,显然十分丑陋,我想看他们每个js组件的具体名字是什么怎么办呢?

  首先找的你的webpack.config.js,然后,加入一个chunkFilename, 

   

  Yes, 就是这样。然后注意到上面的import里面有个注释了吗

  

  这是个啥,Magic Comments, 魔法注释 这个webpackChunkName可以告诉webpack 每个 chunkname 是什么,这里我[request]表示的意思是每次请求的组件名称作为chunkname , 

  本文正文结束啦。

  顺便提一句,如果你的 Magic Comments 不生效注意你的.babelrc 或者tsconfig.json里是否有去掉注释的逻辑(类似removeComments: true ),有的话需要关掉,然后就可以完美按需加载你的组件喽。

 

                                                                      注:本文出自博客园 https://home.cnblogs.com/u/mdengcc/ ,转载请注明出处。 稍微尊重一下原创OK?

原文出处:https://www.cnblogs.com/mdengcc/p/11128886.html

react webpack 按需加载初试

react webpack 按需加载初试

react webpack 按需加载初试

1:为什么要按需加载

  • 【直接打包】React实现单页面运用时,当项目较小或者页面较少的情况下可以无需打包成多个文件,因为即使前端代码有10M,在通过webpack压缩打包,再通过GZIP压缩后一般也就400k,在一面正常的pc系统中400k的大小是可接受的。
  • 【按需加载】但当你的应用稍微复杂一点点,包括依赖后,打包后的文件都是挺大的。而我们加载的时候,不管那些代码有没有执行到,都会下载下来。对于对系统性能要求较高,对代码有‘洁癖’的开发者来说,任何性能的浪费都是不可接受的,所以 我们 只下载我们需要执行的代码的 话,那么可以节省相当大的流量。也就是我们所说的 按需加载

2:webpack按需加载

官方介绍,这里简单介绍下关键信息
- 按需加载函数

require.ensure(dependencies,callback,chunkName)

这个方法可以实现js的按需加载,分开打包,webpack 管它叫 chunk

  • webpack 对应配置
  • webpack配置文件配置一下chunk文件输出路径

    module.exports = {
    output: {
    chunkFilename: ‘[name].[chunkhash:8].chunk.js’,
    publicPath: ‘/dist/’
    }
    }
    [name] 默认是 ID,如果指定了chunkName则为指定的名字。
    [chunkhash] 是对当前chunk 经过hash后得到的值,可以保证在chunk没有变化的时候hash不变,文件不需要更新,chunk变了后,可保证hash唯一,由于hash太长,这里我截取了hash的8个字符足矣

3:react-router 按需加载Demo

<Router history={hashHistory}>
            <Route path="/" component={RootContainer}>
                <Route path="RouterOne"
                       getComponent={
                                (nextState,callback)=>{
                                    require.ensure([],(require)=>{
                                        callback(null,require("../containers/RouterOne").default)
                                    },"router_one")
                                }}
                />
                <Route path="RouterTwo"
                       getComponent={
                                (nextState,require("../containers/RouterTwo").default)
                                    },"router_two")
                                }}
                />
        </Router>

将会打包成bundle.js,router_one_xxxxx_chunk.js,router_two_xxxxx_chunk.js,三个文件

4:前后端分离的项目中存在的问题

  • 当你只打包成一个文件时,每次项目迭代,后端都只需要更改一个文件的路径或者资源
  • 当你使用webpack react-router按需打包时,此时将会生成N个资源文件,每次迭代都需要更新多个资源,容易出错,增大人为出错概率

react webpack配置 paths.js

react webpack配置 paths.js

'use strict';

const path = require('path');
const fs = require('fs');
const getPublicUrlOrPath = require('react-dev-utils/getPublicUrlOrPath');

//项目根目录
const appDirectory = fs.realpathSync(process.cwd());
//生成绝对路径
const resolveApp = relativePath => path.resolve(appDirectory, relativePath);

// 应用程序的公共路径。
//所有资源的公共访问:/
const publicUrlOrPath = getPublicUrlOrPath(
  process.env.NODE_ENV === 'development',
  require(resolveApp('package.json')).homepage,
  process.env.PUBLIC_URL
);
//定义扩展名,这些扩展名会被react解析
const modulefileExtensions = [
  'web.mjs',
  'mjs',
  'web.js',
  'js',
  'web.ts',
  'ts',
  'web.tsx',
  'tsx',
  'json',
  'web.jsx',
  'jsx',
];

// 解析模块的方法
const resolveModule = (resolveFn, filePath) => {
  const extension = modulefileExtensions.find(extension =>
    fs.existsSync(resolveFn(`${filePath}.${extension}`))
  );

  if (extension) {
    return resolveFn(`${filePath}.${extension}`);
  }

  return resolveFn(`${filePath}.js`);
};

// 向外导出各种路径
module.exports = {
  dotenv: resolveApp('.env'),
  appPath: resolveApp('.'),
  appBuild: resolveApp('build'),
  appPublic: resolveApp('public'),
  appHtml: resolveApp('public/index.html'),
  appIndexJs: resolveModule(resolveApp, 'src/index'),
  appPackageJson: resolveApp('package.json'),
  appSrc: resolveApp('src'),
  appTsConfig: resolveApp('tsconfig.json'),
  appJsConfig: resolveApp('jsconfig.json'),
  yarnLockFile: resolveApp('yarn.lock'),
  testsSetup: resolveModule(resolveApp, 'src/setupTests'),
  proxySetup: resolveApp('src/setupProxy.js'),
  appNodeModules: resolveApp('node_modules'),
  publicUrlOrPath,
};



module.exports.modulefileExtensions = modulefileExtensions;

今天关于关于在reactjs项目中如何用webpack配置组件按需加载react 配置webpack的讲解已经结束,谢谢您的阅读,如果想了解更多关于create-react-app脚手架创建react项目,暴露webpack配置文件,如何引入less支持+antd按需加载+自定义主题、React Router 4.0 + webpack 实现组件按需加载、react webpack 按需加载初试、react webpack配置 paths.js的相关知识,请在本站搜索。

本篇文章给大家谈谈入门Webpack,看这篇就够了,以及webpack入门教程的知识点,同时本文还将给你拓展C++内存问题,看这篇就够了、Docker入门,看这篇就够了、ES6入门,看这篇就够了、JS正则表达式入门,看这篇就够了等相关知识,希望对各位有所帮助,不要忘了收藏本站喔。

本文目录一览:

入门Webpack,看这篇就够了(webpack入门教程)

入门Webpack,看这篇就够了(webpack入门教程)

2017年8月13日更新,本文依旧最新的webpack3.5.3将代码部分完全重写,所有代码都在Mac上正常运行过。希望依旧对你学习webpack有帮助。

写在前面的话

阅读本文之前,先看下面这个webpack的配置文件,如果每一项你都懂,那本文能带给你的收获也许就比较有限,你可以快速浏览或直接跳过;如果你和十天前的我一样,对很多选项存在着疑惑,那花一段时间慢慢阅读本文,你的疑惑一定一个一个都会消失;如果你以前没怎么接触过Webpack,而你又你对webpack感兴趣,那么动手跟着本文中那个贯穿始终的例子写一次,写完以后你会发现你已明明白白的走进了Webpack的大门。

// 一个常见的`webpack`配置文件
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {
    entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件
    output: {
        path: __dirname + "/build",filename: "bundle-[hash].js"
    },devtool: 'none',devServer: {
        contentBase: "./public",//本地服务器所加载的页面所在的目录
        historyApiFallback: true,//不跳转
        inline: true,hot: true
    },module: {
        rules: [
            {
                test: /(\.jsx|\.js)$/,use: {
                    loader: "babel-loader"
                },exclude: /node_modules/
            },{
                test: /\.css$/,use: [
                    {
                        loader: "style-loader"
                    },{
                        loader: "css-loader",options: {
                            modules: true
                        }
                    },{
                        loader: "postcss-loader"
                    }
                ]
            }
        ]
    },plugins: [
        new webpack.BannerPlugin('版权所有,翻版必究'),new HtmlWebpackPlugin({
            template: __dirname + "/app/index.tmpl.html"//new 一个这个插件的实例,并传入相关的参数
        }),new webpack.optimize.OccurrenceOrderPlugin(),new webpack.optimize.UglifyJsPlugin(),new ExtractTextPlugin("style.css")

    ],};

什么是WebPack,为什么要使用它?

为什要使用WebPack

现今的很多网页其实可以看做是功能丰富的应用,它们拥有着复杂的JavaScript代码和一大堆依赖包。为了简化开发的复杂度,前端社区涌现出了很多好的实践方法

  • 模块化,让我们可以把复杂的程序细化为小的文件;
  • 类似于TypeScript这种在JavaScript基础上拓展的开发语言:使我们能够实现目前版本的JavaScript不能直接使用的特性,并且之后还能转换为JavaScript文件使浏览器可以识别;
  • Scss,less等CSS预处理器
  • ...

这些改进确实大大的提高了我们的开发效率,但是利用它们开发的文件往往需要进行额外的处理才能让浏览器识别,而手动处理又是非常繁琐的,这就为WebPack类的工具的出现提供了需求。

什么是Webpack

WebPack可以看做是模块打包机:它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其转换和打包为合适的格式供浏览器使用。

WebPack和Grunt以及Gulp相比有什么特性

其实Webpack和另外两个并没有太多的可比性,Gulp/Grunt是一种能够优化前端的开发流程的工具,而WebPack是一种模块化的解决方案,不过Webpack的优点使得Webpack在很多场景下可以替代Gulp/Grunt类的工具。

Grunt和Gulp的工作方式是:在一个配置文件中,指明对某些文件进行类似编译,组合,压缩等任务的具体步骤,工具之后可以自动替你完成这些任务。


Grunt和Gulp的工作流程

Webpack的工作方式是:把你的项目当做一个整体,通过一个给定的主文件(如:index.js),Webpack将从这个文件开始找到你的项目的所有依赖文件,使用loaders处理它们,最后打包为一个(或多个)浏览器可识别的JavaScript文件。


Webpack工作方式

如果实在要把二者进行比较,Webpack的处理速度更快更直接,能打包更多不同类型的文件。

开始使用Webpack

初步了解了Webpack工作方式后,我们一步步的开始学习使用Webpack。

安装

Webpack可以使用npm安装,新建一个空的练习文件夹(此处命名为webpack sample project),在终端中转到该文件夹后执行下述指令就可以完成安装。

//全局安装
npm install -g webpack
//安装到你的项目目录
npm install --save-dev webpack

正式使用Webpack前的准备

  1. 在上述练习文件夹中创建一个package.json文件,这是一个标准的npm说明文件,里面蕴含了丰富的信息,包括当前项目的依赖模块,自定义的脚本任务等等。在终端中使用npm init命令可以自动创建这个package.json文件
npm init

输入这个命令后,终端会问你一系列诸如项目名称,项目描述,作者等信息,不过不用担心,如果你不准备在npm中发布你的模块,这些问题的答案都不重要,回车默认即可。

  1. package.json文件已经就绪,我们在本项目中安装Webpack作为依赖包
// 安装Webpack
npm install --save-dev webpack
  1. 回到之前的空文件夹,并在里面创建两个文件夹,app文件夹和public文件夹,app文件夹用来存放原始数据和我们将写的JavaScript模块,public文件夹用来存放之后供浏览器读取的文件(包括使用webpack打包生成的js文件以及一个index.html文件)。接下来我们再创建三个文件:
  • index.html --放在public文件夹中;
  • Greeter.js-- 放在app文件夹中;
  • main.js-- 放在app文件夹中;

此时项目结构如下图所示


项目结构

我们在index.html文件中写入最基础的HTML代码,它在这里目的在于引入打包后的js文件(这里我们先把之后打包后的js文件命名为bundle.js,之后我们还会详细讲述)。

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <Meta charset="utf-8">
    <title>Webpack Sample Project</title>
  </head>
  <body>
    <div id='root'>
    </div>
    <script src="bundle.js"></script>
  </body>
</html>

我们在Greeter.js中定义一个返回包含问候信息的html元素的函数,并依据Commonjs规范导出这个函数为一个模块:

// Greeter.js
module.exports = function() {
  var greet = document.createElement('div');
  greet.textContent = "Hi there and greetings!";
  return greet;
};

main.js文件中我们写入下述代码,用以把Greeter模块返回的节点插入页面。

//main.js 
const greeter = require('./Greeter.js');
document.querySelector("#root").appendChild(greeter());

正式使用Webpack

webpack可以在终端中使用,在基本的使用方法如下:

# {extry file}出填写入口文件的路径,本文中就是上述main.js的路径,
# {destination for bundled file}处填写打包文件的存放路径
# 填写路径的时候不用添加{}
webpack {entry file} {destination for bundled file}

指定入口文件后,webpack将自动识别项目所依赖的其它文件,不过需要注意的是如果你的webpack不是全局安装的,那么当你在终端中使用此命令时,需要额外指定其在node_modules中的地址,继续上面的例子,在终端中输入如下命令

# webpack非全局安装的情况
node_modules/.bin/webpack app/main.js public/bundle.js

结果如下

使用命令行打包

可以看出webpack同时编译了main.jsGreeter,js,现在打开index.html,可以看到如下结果

htmlResult1

有没有很激动,已经成功的使用Webpack打包了一个文件了。不过在终端中进行复杂的操作,其实是不太方便且容易出错的,接下来看看Webpack的另一种更常见的使用方法。

通过配置文件来使用Webpack

Webpack拥有很多其它的比较高级的功能(比如说本文后面会介绍的loadersplugins),这些功能其实都可以通过命令行模式实现,但是正如前面提到的,这样不太方便且容易出错的,更好的办法是定义一个配置文件,这个配置文件其实也是一个简单的JavaScript模块,我们可以把所有的与打包相关的信息放在里面。

继续上面的例子来说明如何写这个配置文件,在当前练习文件夹的根目录下新建一个名为webpack.config.js的文件,我们在其中写入如下所示的简单配置代码,目前的配置主要涉及到的内容是入口文件路径和打包后文件的存放路径。

module.exports = {
  entry:  __dirname + "/app/main.js",//已多次提及的唯一入口文件
  output: {
    path: __dirname + "/public",//打包后的文件存放的地方
    filename: "bundle.js"//打包后输出文件的文件名
  }
}

:“__dirname”是node.js中的一个全局变量,它指向当前执行脚本所在的目录。

有了这个配置之后,再打包文件,只需在终端里运行webpack(非全局安装需使用node_modules/.bin/webpack)命令就可以了,这条命令会自动引用webpack.config.js文件中的配置选项,示例如下:

配合配置文件进行打包

又学会了一种使用Webpack的方法,这种方法不用管那烦人的命令行参数,有没有感觉很爽。如果我们可以连webpack(非全局安装需使用node_modules/.bin/webpack)这条命令都可以不用,那种感觉会不会更爽~,继续看下文。

更快捷的执行打包任务

在命令行中输入命令需要代码类似于node_modules/.bin/webpack这样的路径其实是比较烦人的,不过值得庆幸的是npm可以引导任务执行,对npm进行配置后可以在命令行中使用简单的npm start命令来替代上面略微繁琐的命令。在package.json中对scripts对象进行相关设置即可,设置方法如下。

{
  "name": "webpack-sample-project","version": "1.0.0","description": "Sample webpack project","scripts": {
    "start": "webpack" // 修改的是这里,JSON文件不支持注释,引用时请清除
  },"author": "zhang","license": "ISC","devDependencies": {
    "webpack": "^1.12.9"
  }
}

注:package.json中的script会安装一定顺序寻找命令对应位置,本地的node_modules/.bin路径就在这个寻找清单中,所以无论是全局还是局部安装的Webpack,你都不需要写前面那指明详细的路径了。

npm的start命令是一个特殊的脚本名称,其特殊性表现在,在命令行中使用npm start就可以执行其对于的命令,如果对应的此脚本名称不是start,想要在命令行中运行时,需要这样用npm run {script name}npm run build,我们在命令行中输入npm start试试,输出结果如下:

使用npm start 打包代码

现在只需要使用npm start就可以打包文件了,有没有觉得webpack也不过如此嘛,不过不要太小瞧webpack,要充分发挥其强大的功能我们需要修改配置文件的其它选项,一项项来看。

Webpack的强大功能

生成Source Maps(使调试更容易)

开发总是离不开调试,方便的调试能极大的提高开发效率,不过有时候通过打包后的文件,你是不容易找到出错了的地方,对应的你写的代码的位置的,Source Maps就是来帮我们解决这个问题的。

通过简单的配置,webpack就可以在打包时为我们生成的source maps,这为我们提供了一种对应编译文件和源文件的方法,使得编译后的代码可读性更高,也更容易调试。

webpack的配置文件中配置source maps,需要配置devtool,它有以下四种不同的配置选项,各具优缺点,描述如下:

devtool选项 配置结果
source-map 在一个单独的文件中产生一个完整且功能完全的文件。这个文件具有最好的source map,但是它会减慢打包速度;
cheap-module-source-map 在一个单独的文件中生成一个不带列映射的map,不带列映射提高了打包速度,但是也使得浏览器开发者工具只能对应到具体的行,不能对应到具体的列(符号),会对调试造成不便;
eval-source-map 使用eval打包源文件模块,在同一个文件中生成干净的完整的source map。这个选项可以在不影响构建速度的前提下生成完整的sourcemap,但是对打包后输出的JS文件的执行具有性能和安全的隐患。在开发阶段这是一个非常好的选项,在生产阶段则一定不要启用这个选项;
cheap-module-eval-source-map 这是在打包文件时最快的生成source map的方法,生成的Source Map 会和打包后的JavaScript文件同行显示,没有列映射,和eval-source-map选项具有相似的缺点;

正如上表所述,上述选项由上到下打包速度越来越快,不过同时也具有越来越多的负面作用,较快的打包速度的后果就是对打包后的文件的的执行有一定影响。

对小到中型的项目中,eval-source-map是一个很好的选项,再次强调你只应该开发阶段使用它,我们继续对上文新建的webpack.config.js,进行如下配置:

module.exports = {
  devtool: 'eval-source-map',entry:  __dirname + "/app/main.js",output: {
    path: __dirname + "/public",filename: "bundle.js"
  }
}

cheap-module-eval-source-map方法构建速度更快,但是不利于调试,推荐在大型项目考虑时间成本时使用。

使用webpack构建本地服务器

想不想让你的浏览器监听你的代码的修改,并自动刷新显示修改后的结果,其实Webpack提供一个可选的本地开发服务器,这个本地服务器基于node.js构建,可以实现你想要的这些功能,不过它是一个单独的组件,在webpack中进行配置之前需要单独安装它作为项目依赖

npm install --save-dev webpack-dev-server

devserver作为webpack配置选项中的一项,以下是它的一些配置选项,更多配置可参考这里

devserver的配置选项 功能描述
contentBase 默认webpack-dev-server会为根文件夹提供本地服务器,如果想为另外一个目录下的文件提供本地服务器,应该在这里设置其所在目录(本例设置到“public"目录)
port 设置默认监听端口,如果省略,默认为”8080“
inline 设置为true,当源文件改变时会自动刷新页面
historyApiFallback 在开发单页应用时非常有用,它依赖于HTML5 history API,如果设置为true,所有的跳转将指向index.html

把这些命令加到webpack的配置文件中,现在的配置文件webpack.config.js如下所示

module.exports = {
  devtool: 'eval-source-map',filename: "bundle.js"
  },devServer: {
    contentBase: "./public",//本地服务器所加载的页面所在的目录
    historyApiFallback: true,//不跳转
    inline: true//实时刷新
  } 
}

package.json中的scripts对象中添加如下命令,用以开启本地服务器:

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1","start": "webpack","server": "webpack-dev-server --open"
  },

在终端中输入npm run server即可在本地的8080端口查看结果

开启本地服务器

Loaders

鼎鼎大名的Loaders登场了!

Loaderswebpack提供的最激动人心的功能之一了。通过使用不同的loaderwebpack有能力调用外部的脚本或工具,实现对不同格式的文件的处理,比如说分析转换scss为css,或者把下一代的JS文件(ES6,ES7)转换为现代浏览器兼容的JS文件,对React的开发而言,合适的Loaders可以把React的中用到的JSX文件转换为JS文件。

Loaders需要单独安装并且需要在webpack.config.js中的modules关键字下进行配置,Loaders的配置包括以下几方面:

  • test:一个用以匹配loaders所处理文件的拓展名的正则表达式(必须)
  • loader:loader的名称(必须)
  • include/exclude:手动添加必须处理的文件(文件夹)或屏蔽不需要处理的文件(文件夹)(可选);
  • query:为loaders提供额外的设置选项(可选)

不过在配置loader之前,我们把Greeter.js里的问候消息放在一个单独的JSON文件里,并通过合适的配置使Greeter.js可以读取该JSON文件的值,各文件修改后的代码如下:

在public文件夹中创建带有问候信息的JSON文件(命名为config.json)

{
  "greetText": "Hi there and greetings from JSON!"
}

更新后的Greeter.js

var config = require('./config.json');

module.exports = function() {
  var greet = document.createElement('div');
  greet.textContent = config.greetText;
  return greet;
};

由于webpack3.*/webpack2.*已经内置可处理JSON文件,这里我们无需再添加webpack1.*需要的json-loader。在看如何具体使用loader之前我们先看看Babel是什么?

Babel

Babel其实是一个编译JavaScript的平台,它的强大之处表现在可以通过编译帮你达到以下目的:

  • 使用下一代的JavaScript代码(ES6,ES7...),即使这些标准目前并未被当前的浏览器完全的支持;
  • 使用基于JavaScript进行了拓展的语言,比如React的JSX;

Babel的安装与配置

Babel其实是几个模块化的包,其核心功能位于称为babel-core的npm包中,webpack可以把其不同的包整合在一起使用,对于每一个你需要的功能或拓展,你都需要安装单独的包(用得最多的是解析Es6的babel-preset-es2015包和解析JSX的babel-preset-react包)。

我们先来一次性安装这些依赖包

// npm一次性安装多个依赖模块,模块之间用空格隔开
npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react

webpack中配置Babel的方法如下:

module.exports = {
    entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件
    output: {
        path: __dirname + "/public",//打包后的文件存放的地方
        filename: "bundle.js"//打包后输出文件的文件名
    },devtool: 'eval-source-map',//不跳转
        inline: true//实时刷新
    },use: {
                    loader: "babel-loader",options: {
                        presets: [
                            "es2015","react"
                        ]
                    }
                },exclude: /node_modules/
            }
        ]
    }
};

现在你的webpack的配置已经允许你使用ES6以及JSX的语法了。继续用上面的例子进行测试,不过这次我们会使用React,记得先安装 React 和 React-DOM

npm install --save react react-dom

接下来我们使用ES6的语法,更新Greeter.js并返回一个React组件

//Greeter,js
import React,{Component} from 'react'
import config from './config.json';

class Greeter extends Component{
  render() {
    return (
      <div>
        {config.greetText}
      </div>
    );
  }
}

export default Greeter

修改main.js如下,使用ES6的模块定义和渲染Greeter模块

import React from 'react';
import {render} from 'react-dom';
import Greeter from './Greeter';

render(<Greeter />,document.getElementById('root'));

重新使用npm start打包,如果之前打开的本地服务器没有关闭,你应该可以在localhost:8080下看到与之前一样的内容,这说明reactes6被正常打包了。

localhost:8080

Babel的配置

Babel其实可以完全在 webpack.config.js 中进行配置,但是考虑到babel具有非常多的配置选项,在单一的webpack.config.js文件中进行配置往往使得这个文件显得太复杂,因此一些开发者支持把babel的配置选项放在一个单独的名为 ".babelrc" 的配置文件中。我们现在的babel的配置并不算复杂,不过之后我们会再加一些东西,因此现在我们就提取出相关部分,分两个配置文件进行配置(webpack会自动调用.babelrc里的babel配置选项),如下:

// webpack.config.js
module.exports = {
  devtool: 'eval-source-map',module: {
    loaders: [
      {
        test: /\.json$/,loader: "json"
      },{
        test: /\.js$/,exclude: /node_modules/,loader: 'babel'
      }
    ]
  },devServer: {...} // Omitted for brevity
}
//.babelrc
{
  "presets": ["react","es2015"]
}

到目前为止,我们已经知道了,对于模块,Webpack能提供非常强大的处理功能,那那些是模块呢。

一切皆模块

Webpack有一个不可不说的优点,它把所有的文件都都当做模块处理,JavaScript代码,CSS和fonts以及图片等等通过合适的loader都可以被处理。

CSS

webpack提供两个工具处理样式表,css-loaderstyle-loader,二者处理的任务不同,css-loader使你能够使用类似@importurl(...)的方法实现 require()的功能,style-loader将所有的计算后的样式加入页面中,二者组合在一起使你能够把样式表嵌入webpack打包后的JS文件中。

继续上面的例子

//安装
npm install --save-dev style-loader css-loader
//使用
module.exports = {

   ...
    module: {
        rules: [
            {
                test: /(\.jsx|\.js)$/,{
                        loader: "css-loader"
                    }
                ]
            }
        ]
    }
};

请注意这里对同一个文件引入多个loader的方法。

接下来,在app文件夹里创建一个名字为"main.css"的文件,对一些元素设置样式

/* main.css */
html {
  Box-sizing: border-Box;
  -ms-text-size-adjust: 100%;
  -webkit-text-size-adjust: 100%;
}

*,*:before,*:after {
  Box-sizing: inherit;
}

body {
  margin: 0;
  font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif;
}

h1,h2,h3,h4,h5,h6,p,ul {
  margin: 0;
  padding: 0;
}

我们这里例子中用到的webpack只有单一的入口,其它的模块需要通过 import,require,url等与入口文件建立其关联,为了让webpack能找到”main.css“文件,我们把它导入”main.js “中,如下

//main.js
import React from 'react';
import {render} from 'react-dom';
import Greeter from './Greeter';

import './main.css';//使用require导入css文件

render(<Greeter />,document.getElementById('root'));

通常情况下,css会和js打包到同一个文件中,并不会打包为一个单独的css文件,不过通过合适的配置webpack也可以把css打包为单独的文件的。

上面的代码说明webpack是怎么把css当做模块看待的,咱们继续看一个更加真实的css模块实践。

CSS module

在过去的一些年里,JavaScript通过一些新的语言特性,更好的工具以及更好的实践方法(比如说模块化)发展得非常迅速。模块使得开发者把复杂的代码转化为小的,干净的,依赖声明明确的单元,配合优化工具,依赖管理和加载管理可以自动完成。

不过前端的另外一部分,CSS发展就相对慢一些,大多的样式表却依旧巨大且充满了全局类名,维护和修改都非常困难。

最近有一个叫做 CSS modules 的技术就意在把JS的模块化思想带入CSS中来,通过CSS模块,所有的类名,动画名默认都只作用于当前模块。Webpack从一开始就对CSS模块化提供了支持,在CSS loader中进行配置后,你所需要做的一切就是把”modules“传递到所需要的地方,然后就可以直接把CSS的类名传递到组件的代码中,且这样做只对当前组件有效,不必担心在不同的模块中使用相同的类名造成冲突。具体的代码如下

module.exports = {

    ...

    module: {
        rules: [
            {
                test: /(\.jsx|\.js)$/,options: {
                            modules: true
                        }
                    }
                ]
            }
        ]
    }
};

在app文件夹下创建一个Greeter.css文件

.root {
  background-color: #eee;
  padding: 10px;
  border: 3px solid #ccc;
}

导入.root到Greeter.js中

import React,{Component} from 'react';
import config from './config.json';
import styles from './Greeter.css';//导入

class Greeter extends Component{
  render() {
    return (
      <div className={styles.root}>//添加类名
        {config.greetText}
      </div>
    );
  }
}

export default Greeter

放心使用把,相同的类名也不会造成不同组件之间的污染。

应用了css module后的样式

CSS modules 也是一个很大的主题,有兴趣的话可以去官方文档查看更多消息

CSS预处理器

SassLess 之类的预处理器是对原生CSS的拓展,它们允许你使用类似于variables,nesting,mixins,inheritance等不存在于CSS中的特性来写CSS,CSS预处理器可以这些特殊类型的语句转化为浏览器可识别的CSS语句,

你现在可能都已经熟悉了,在webpack里使用相关loaders进行配置就可以使用了,以下是常用的CSS 处理loaders:

  • Less Loader
  • Sass Loader
  • Stylus Loader

不过其实也存在一个CSS的处理平台-PostCSS,它可以帮助你的CSS实现更多的功能,在其官方文档可了解更多相关知识。

举例来说如何使用PostCSS,我们使用PostCSS来为CSS代码自动添加适应不同浏览器的CSS前缀。

首先安装postcss-loaderautoprefixer(自动添加前缀的插件)

npm install --save-dev postcss-loader autoprefixer

接下来,在webpack配置文件中添加postcss-loader,在根目录新建postcss.config.js,并添加如下代码之后,重新使用npm start打包时,你写的css会自动根据Can i use里的数据添加不同前缀了。

//webpack.config.js
module.exports = {
    ...
    module: {
        rules: [
            {
                test: /(\.jsx|\.js)$/,{
                        loader: "postcss-loader"
                    }
                ]
            }
        ]
    }
}
// postcss.config.js
module.exports = {
    plugins: [
        require('autoprefixer')
    ]
}

至此,本文已经谈论了处理JS的Babel和处理CSS的PostCSS的基本用法,它们其实也是两个单独的平台,配合webpack可以很好的发挥它们的作用。接下来介绍Webpack中另一个非常重要的功能-Plugins

插件(Plugins)

插件(Plugins)是用来拓展Webpack功能的,它们会在整个构建过程中生效,执行相关的任务。
Loaders和Plugins常常被弄混,但是他们其实是完全不同的东西,可以这么来说,loaders是在打包构建过程中用来处理源文件的(JSX,Scss,Less..),一次处理一个,插件并不直接操作单个文件,它直接对整个构建过程其作用。

Webpack有很多内置插件,同时也有很多第三方插件,可以让我们完成更加丰富的功能。

使用插件的方法

要使用某个插件,我们需要通过npm安装它,然后要做的就是在webpack配置中的plugins关键字部分添加该插件的一个实例(plugins是一个数组)继续上面的例子,我们添加了一个给打包后代码添加版权声明的插件。

const webpack = require('webpack');

module.exports = {
...
    module: {
        rules: [
            {
                test: /(\.jsx|\.js)$/,plugins: [
        new webpack.BannerPlugin('版权所有,翻版必究')
    ],};

通过这个插件,打包后的JS文件显示如下

版权所有,翻版必究

这就是webpack插件的基础用法了,下面给大家推荐几个常用的插件

HtmlWebpackPlugin

这个插件的作用是依据一个简单的index.html模板,生成一个自动引用你打包后的JS文件的新index.html。这在每次生成的js文件名称不同时非常有用(比如添加了hash值)。

安装

npm install --save-dev html-webpack-plugin

这个插件自动完成了我们之前手动做的一些事情,在正式使用之前需要对一直以来的项目结构做一些更改:

  1. 移除public文件夹,利用此插件,index.html文件会自动生成,此外CSS已经通过前面的操作打包到JS中了。
  2. 在app目录下,创建一个index.tmpl.html文件模板,这个模板包含title等必须元素,在编译过程中,插件会依据此模板生成最终的html页面,会自动添加所依赖的 css,js,favicon等文件,index.tmpl.html中的模板源代码如下:
<!DOCTYPE html>
<html lang="en">
  <head>
    <Meta charset="utf-8">
    <title>Webpack Sample Project</title>
  </head>
  <body>
    <div id='root'>
    </div>
  </body>
</html>

3.更新webpack的配置文件,方法同上,新建一个build文件夹用来存放最终的输出文件

const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: __dirname + "/app/main.js",filename: "bundle.js"
    },new HtmlWebpackPlugin({
            template: __dirname + "/app/index.tmpl.html"//new 一个这个插件的实例,并传入相关的参数
        })
    ],};

再次执行npm start你会发现,build文件夹下面生成了bundle.jsindex.html

build文件夹

Hot Module Replacement

Hot Module Replacement(HMR)也是webpack里很有用的一个插件,它允许你在修改组件代码后,自动刷新实时预览修改后的效果。

在webpack中实现HMR也很简单,只需要做两项配置

  1. 在webpack配置文件中添加HMR插件;
  2. 在Webpack Dev Server中添加“hot”参数;

不过配置完这些后,JS模块其实还是不能自动热加载的,还需要在你的JS模块中执行一个Webpack提供的API才能实现热加载,虽然这个API不难使用,但是如果是React模块,使用我们已经熟悉的Babel可以更方便的实现功能热加载。

整理下我们的思路,具体实现方法如下

  • Babelwebpack是独立的工具
  • 二者可以一起工作
  • 二者都可以通过插件拓展功能
  • HMR是一个webpack插件,它让你能浏览器中实时观察模块修改后的效果,但是如果你想让它工作,需要对模块进行额外的配额;
  • Babel有一个叫做react-transform-hrm的插件,可以在不对React模块进行额外的配置的前提下让HMR正常工作;

还是继续上例来实际看看如何配置

const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: __dirname + "/app/main.js",new webpack.HotModuleReplacementPlugin()//热加载插件
    ],};

安装react-transform-hmr

npm install --save-dev babel-plugin-react-transform react-transform-hmr

配置Babel

// .babelrc
{
  "presets": ["react","es2015"],"env": {
    "development": {
    "plugins": [["react-transform",{
       "transforms": [{
         "transform": "react-transform-hmr","imports": ["react"],"locals": ["module"]
       }]
     }]]
    }
  }
}

现在当你使用React时,可以热加载模块了,每次保存就能在浏览器上看到更新内容。

产品阶段的构建

目前为止,我们已经使用webpack构建了一个完整的开发环境。但是在产品阶段,可能还需要对打包的文件进行额外的处理,比如说优化,压缩,缓存以及分离CSS和JS。

对于复杂的项目来说,需要复杂的配置,这时候分解配置文件为多个小的文件可以使得事情井井有条,以上面的例子来说,我们创建一个webpack.production.config.js的文件,在里面加上基本的配置,它和原始的webpack.config.js很像,如下

// webpack.production.config.js
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: __dirname + "/app/main.js",};
//package.json
{
  "name": "test","description": "","main": "index.js","scripts": {
    "test": "echo \"Error: no test specified\" && exit 1","server": "webpack-dev-server --open","build": "NODE_ENV=production webpack --config ./webpack.production.config.js --progress"
  },"author": "","devDependencies": {
...
  },"dependencies": {
    "react": "^15.6.1","react-dom": "^15.6.1"
  }
}

优化插件

webpack提供了一些在发布阶段非常有用的优化插件,它们大多来自于webpack社区,可以通过npm安装,通过以下插件可以完成产品发布阶段所需的功能

  • OccurenceOrderPlugin :为组件分配ID,通过这个插件webpack可以分析和优先考虑使用最多的模块,并为它们分配最小的ID
  • UglifyJsPlugin:压缩JS代码;
  • ExtractTextPlugin:分离CSS和JS文件

我们继续用例子来看看如何添加它们,OccurenceOrder 和 UglifyJS plugins 都是内置插件,你需要做的只是安装其它非内置插件

npm install --save-dev extract-text-webpack-plugin

在配置文件的plugins后引用它们

// webpack.production.config.js
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {
    entry: __dirname + "/app/main.js",new HtmlWebpackPlugin({
            template: __dirname + "/app/index.tmpl.html"
        }),new ExtractTextPlugin("style.css")
    ],};

此时执行npm run build可以看见代码是被压缩后的

压缩后的代码

缓存

缓存无处不在,使用缓存的最好方法是保证你的文件名和文件内容是匹配的(内容改变,名称相应改变)

webpack可以把一个哈希值添加到打包的文件名中,使用方法如下,添加特殊的字符串混合体([name],[id] and [hash])到输出文件名前

const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {
..
    output: {
        path: __dirname + "/build",...
};

现在用户会有合理的缓存了。

带hash值的js名

总结

其实这是一年前的文章了,趁周末重新运行和修改了一下,现在所有的代码都可以正常运行,所用webpack基于最新的webpack3.5.3。希望依旧能对你有帮助。

这是一篇好长的文章,谢谢你的耐心,能仔细看到了这里,大概半个月前我第一次自己一步步配置项目所需的Webpack后就一直想写一篇笔记做总结,几次动笔都不能让自己满意,总觉得写不清楚。直到看到本文的英文版Webpack for React,真的有多次豁然开朗的感觉,喜欢看原文的点链接就可以看了。其实关于Webpack本文讲述得仍不完全,不过相信你看完后已经进入Webpack的大门,能够更好的探索其它的关于Webpack的知识了。

欢迎大家在文后发表自己的观点讨论。

作者:zhangwang 链接:http://www.jianshu.com/p/42e11515c10f 來源:简书 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

C++内存问题,看这篇就够了

C++内存问题,看这篇就够了

导语 深入理解C++内存管理,一文了解所有C++内存问题,万字长文,建议收藏

关注公众号「职场重生」,非常多的干活文章,让你醍醐灌顶,少走几年弯路;

随着人工智能,云计算等技术的迅猛发展,让Python,go等新兴语言流行了起来,很多人以为C++可能已经过时了,确实,C++编程语言走到今天已经有将近40年的历史了,但它依然是当今的主流语言,我们可以看一下世界权威编程语言排行榜,C++依然是属于第一梯队,C++在金融交易系统,游戏,数据库,编译器,大型桌面程序,高性能服务器,浏览器,各类编程比赛(ACM-ICPC,Topcoder,Codeforces,Google Code Jam)等领域任然是主力军。

在各个大厂情况,C++也是很多大厂主力编程语言,国外google和微软大部分核心产品都是基于C++开发的;鹅厂编程语言TOP5,C++排第一:

C++的高抽象层次,又兼具高性能,是其他语言所无法替代的,C++标准保持稳定发展,更加现代化,更加强大,更加易用,熟练的 C++ 工程师自然也获得了“高水平、高薪资”的名声,但在各种活跃编程语言中,C++门槛依然很高,尤其C++的内存问题(内存泄露,内存溢出,内存宕机,堆栈破坏等问题),需要理解C++标准对象模型,C++标准库,标准C库,操作系统等内存设计,才能更加深入理解C++内存管理,这是跨越C++三座大山之一,我们必须拿下它。

Content

环境:

uname -a
Linux alexfeng 3.19.0-15-generic #15-Ubuntu SMP Thu Apr 16 23:32:37 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux
cat /proc/cpuinfo
bugs            :
bogomips        : 4800.52
clflush size    : 64
cache_alignment : 64
address sizes   : 36 bits physical, 48 bits virtual
cat /proc/meminfo
MemTotal:        4041548 kB(4G)
MemFree:          216304 kB
MemAvailable:    2870340 kB
Buffers:          983360 kB
Cached:          1184008 kB
SwapCached:        54528 kB
GNU gdb (Ubuntu 7.9-1ubuntu1) 7.9
g++ (Ubuntu 4.9.2-10ubuntu13) 4.9.2

-

一  C++内存模型

C++11在标准库中引入了memory model,这应该是C++11最重要的特性之一了。C++11引入memory model的意义在于我们可以在high level language层面实现对在多处理器中多线程共享内存交互的控制。我们可以在语言层面忽略compiler,CPU arch的不同对多线程编程的影响了。我们的多线程可以跨平台。

内存模型

为 C++ 定义计算机内存存储的语义。可用于 C++ 程序的内存是一或多个相接的字节序列。内存中的每个字节拥有唯一的地址。

字节

字节是最小的可寻址内存单元。它被定义为相接的位序列,大到足以保有任何 UTF-8 编码单元( 256 个相异值)和 (C++14 起)基本执行字符集(要求为单字节的 96 个字符)的任何成员。类似 C , C++ 支持 8 位或更大的字节。char 、 unsigned char 和 signed char 类型把一个字节用于存储和值表示。字节中的位数可作为 CHAR_BIT 或 std::numeric_limits<unsigned char>::digits 访问。

内存位置

内存位置是

  • 一个标量类型(算术类型、指针类型、枚举类型或 std::nullptr_t )对象
  • 或非零长位域的最大相接序列

注意:各种语言特性,例如引用和虚函数,可能涉及到程序不可访问,但为实现所管理的额外内存位置。

线程与数据竞争

  • 执行线程是程序中的控制流,它始于 std::thread::thread 、 std::async 或以其他方式所做的顶层函数调用。
  • 任何线程都能潜在地访问程序中的任何对象(拥有自动或线程局域存储期的对象仍可为另一线程通过指针或引用访问)。
  • 始终允许不同的执行线程同时访问(读和写)不同的内存位置,而无冲突或同步要求。

一个表达式的求值写入内存位置,而另一求值读或写同一内存位置时,称这些表达式冲突。拥有二个冲突求值的程序有数据竞争,除非

  • 两个求值都在同一线程上,或同一信号处理函数中执行,或
  • 两个冲突求值都是原子操作(见 std::atomic ),或
  • 一个冲突求值先发生于( happens-before )另一个(见内存顺序--std::memory_order )

若出现数据竞争,则程序的行为未定义。

内存顺序(std::memory_order)

如果不使用任何同步机制(例如 mutex 或 atomic),在多线程中读写同一个变量,那么程序的结果是难以预料的。简单来说,编译器以及 CPU 的一些行为,会影响到C++程序的执行结果

  • 即使是简单的语句,C++ 也不保证是原子操作。
  • CPU 可能会调整指令的执行顺序。
  • 在 CPU cache 的影响下,一个 CPU 执行了某个指令,不会立即被其它 CPU 看见。
  • Intel x86, x86-64等属于强排序CPU,x86-64的强内存模型总能保证按顺序执行,遵从数据依赖顺序,但PowerPC和ARM是弱排序CPU,有时需要依赖内存栅栏指令。

多线程读写同一变量需要使用同步机制,最常见的同步机制就是std::mutexstd::atomic。然而从性能角度看,通常使用std::atomic会获得更好的性能.

C++11 提供6 种可以应用于原子变量的内存次序:

  • momory_order_relaxed,
  • memory_order_consume,
  • memory_order_acquire,
  • memory_order_release,
  • memory_order_acq_rel,
  • memory_order_seq_cst

虽然共有 6 个选项,但它们表示的是四种内存模型:

  • Relaxed ordering
  • Release-Acquire ordering
  • Release-Consume ordering
  • Sequentially-consistent ordering

顺序一致次序(sequential consisten ordering)

对应memory_order_seq_cst. SC作为默认的内存序,是因为它意味着将程序看做是一个简单的序列。如果对于一个原子变量的操作都是顺序一致的,那么多线程程序的行为就像是这些操作都以一种特定顺序被单线程程序执行。从同的角度来看,一个顺序一致的 store 操作 synchroniezd-with 一个顺序一致的需要读取相同的变量的 load 操作。除此以外,顺序模型还保证了在 load 之后执行的顺序一致原子操作都得表现得在 store 之后完成。非顺序一致内存次序(non-sequentially consistency memory ordering)强调对同一事件(代码),不同线程可以以不同顺序去执行,不仅是因为编译器可以进行指令重排,也因为不同的 CPU cache 及内部缓存的状态可以影响这些指令的执行。但所有线程仍需要对某个变量的连续修改达成顺序一致。

松弛次序(relaxed ordering)

在这种模型下,std::atomicload()store()都要带上memory_order_relaxed参数。Relaxed ordering 仅仅保证load()store()是原子操作,除此之外,不提供任何跨线程的同步。

获取-释放次序(acquire-release ordering)

在这种模型下,store()使用memory_order_release,而load()使用memory_order_acquire。这种模型有两种效果,第一种是可以限制 CPU 指令的重排:

  • store()之前的所有读写操作,不允许被移动到这个store()的后面。
  • load()之后的所有读写操作,不允许被移动到这个load()的前面。

数据依赖(Release-Consume ordering)

memory_order_consume 是 acquire-release 顺序模型中的一种,但它比较特殊,它为 inter-thread happens-before 引入了数据依赖关系:dependency-ordered-before ,一个使用memory_order_consume的操作具有消费语义(consume semantics)。我们称这个操作为消费操作(consume operations),对于memory_order_consume最的价值的观察结果就是总是可以安全的将它替换成memory_order_acquire,消费和获取都为了同一个目的:帮助非原子信息在线程间安全的传递。就像获取操作一样,消费操作必须与另一个线程的释放操作一起使用。它们之间主要的区别在于消费操作可以正确起作用的案例更少。相对于它的使用不便,反过来也就意味着消费操作在某些平台使用更有效。

默认情况下,std::atomic使用的是 Sequentially-consistent ordering。但在某些场景下,合理使用其它三种 ordering,可以让编译器优化生成的代码,从而提高性能。

思考问题:

1  C++正常程序可以访问到哪些内存和不能访问到哪些内存(这些内存属于该程序)?

2  内存对程序并发执行有什么影响?

3  std::memory_order 的作用是什么?

二  C++对象内存模型

1 空类对象(一般作为模板的tag来使用)

class A { };

sizeof(A) = 1

C++标准要求C++的对象大小不能为0,C++对象必须在内存里面有唯一的地址,

但又不想浪费太多内存空间,所以标准规定为1byte,

2  非空类

class A
{
public:
 int a;
};
sizeof(A ) = 8  ,align=8

3  非空虚基类

class A
{
public:
 int a;
 virtual void v();
};
sizeof(A ) = 16  ,align=8

4 单继承

class A {
public:
 int a;
 virtual void v();
};
class B : public A {
public:
 int b;
};
sizeof(B) = 16, align = 8

                  

5 简单多继承

class A {
public:
 int a;
 virtual void v();
};
class B {
public:
 int b;
 virtual void w();
};
class C : public A, public B {
public:
 int c;
};
​
​
sizeof(C) = 32 ,align = 8

6 简单多继承-2

class A {
public:
 int a;
 virtual void v();
};
class B {
public:
 int b;
 virtual void w();
};
class C : public A, public B {
public:
 int c;
 void w();
};
sizeof(C) = 32 ,align = 8

7 The Diamond: 多重继承 (没有虚继承)

class A {
public:
 int a;
 virtual void v();
};
class B : public A {
public:
 int b;
 virtual void w();
};
class C : public A {
public:
 int c;
 virtual void x();
};
class D : public B, public C {
public:
 int d;
 virtual void y();
};
sizeof(D)  = 40 align = 8

                      

注意点:此种继承存在两份基类成员,使用时候需要指定路径,不方便,易出错。

8 The Diamond: 钻石类虚继承

解决上面的问题,让基类只有存在一份,共享基类;

class A {
public:
 int a;
 virtual void v();
};
​
class B : public virtual A {
public:
 int b;
 virtual void w();
};
​
class C : public virtual A {
public:
 int c;
 virtual void x();
};
​
class D : public B, public C {
public:
 int d;
 virtual void y();
};
​
​
sizeof(D) = 48,align = 8

注意点:

1.top_offset 表示this指针对子类的偏移,用于子类和继承类之间dynamic_cast转换(还需要typeinfo数据),实现多态,

vbase_offset 表示this指针对基类的偏移,用于共享基类;

2.gcc为了每一个类生成一个vtable虚函数表,放在程序的.rodata段,其他编译器(平台)比如vs,实现不太一样.

3.gcc还有VTT表,里面存放了各个基类之间虚函数表的关系,最大化利用基类的虚函数表,专门用来为构建最终类vtable;

4.在构造函数里面设置对象的vtptr指针。

5.虚函数表地址的前面设置了一个指向type_info的指针,RTTI(Run Time Type Identification)运行时类型识别是有编译器在编译器生成的特殊类型信息,包括对象继承关系,对象本身的描述,RTTI是为多态而生成的信息,所以只有具有虚函数的对象在会生成。

6.在C++类中有两种成员数据:static、nonstatic;三种成员函数:static、nonstatic、virtual。

C++成员非静态数据需要占用动态内存,栈或者堆中,其他static数据存在全局变量区(数据段),编译时候确定。虚函数会增加用虚函数表大小,也是存储在数据区的.rodada段,编译时确定,其他函数不占空间。

7.G++选项 -fdump-class-hierarchy 可以生成C++类层结构,虚函数表结构,VTT表结构。

8.GDB调试选项:

set p obj <on/off> :在C++中,如果一个对象指针指向其派生类, 如果打开这个选项,GDB会现在类对象结构的规则显示输出。

set p pertty <on/off>:   按照层次打印结构体。

思考问题:

1 Why don''t we have virtual constructors?

From Bjarne Stroustrup''s C++ Style and Technique FAQ

A virtual call is a mechanism to get work done given partial information. In particular, "virtual" allows us to call a function knowing only any interfaces and not the exact type of the object. To create an object you need complete information. In particular, you need to know the exact type of what you want to create. Consequently, a "call to a constructor" cannot be virtual.

2  为什么不要在构造函数或者析构函数中调用虚函数?

对于构造函数:此时子类的对象还没有完全构造,编译器会去虚函数化,只会用当前类的函数, 如果是纯虚函数,就会调用到纯虚函数,会导致构造函数抛异常:pure virtual method calle;对于析构函数:同样,由于对象不完整,编译器会去虚函数化,函数调用本类的虚函数,如果本类虚函数是纯虚函数,就会到账析构函数抛出异常:  pure virtual method called;

3  C++对象构造顺序?

1.构造子类构造函数的参数

2.子类调用基类构造函数

3.基类设置vptr

4.基类初始化列表内容进行构造

5.  基类函数体调用

6.  子类设置vptr

7.  子类初始化列表内容进行构造

8.  子类构造函数体调用

4  为什么虚函数会降低效率?

是因为虚函数调用执行过程中会跳转两次,首先找到虚函数表,然后再查找对应函数地址,这样CPU指令就会跳转两次,而普通函数指跳转一次,CPU每跳转一次,预取指令都可能作废,这会导致分支预测失败,流水线排空,所以效率会变低。设想一下,如果说不是虚函数,那么在编译时期,其相对地址是确定的,编译器可以直接生成jmp/invoke指令;如果是虚函数,多出来的一次查找vtable所带来的开销,倒是次要的,关键在于,这个函数地址是动态的,譬如 取到的地址在eax里,则在call eax之后的那些已经被预取进入流水线的所有指令都将失效。流水线越长,一次分支预测失败的代价也就越大。

三  C++程序运行内存空间模型

1. C++程序大致运行内存空间:

32位:

64位:

2  Linux虚拟内存内部实现

关键点:

1 各个分区的意义

内核空间:在32位系统中,Linux会留1G空间给内核,用户进程是无法访问的,用来存放进程相关数据和内存数据,内核代码等;在64位系统里面,Linux会采用最低48位来表示虚拟内存,这可通过 /proc/cpuinfo 来查看address sizes :

address sizes   : 36 bits physical, 48 bits virtual,总的虚拟地址空间为256TB( 2^48 ),在这256TB的虚拟内存空间中, 0000000000000000 - 00007fffffffffff(128TB)为用户空间,ffff800000000000 - ffffffffffffffff(128TB)为内核空间。目前常用的分配设计:

Virtual memory map with 4 level page tables:
0000000000000000 - 00007fffffffffff (=47 bits) user space, different per mm
hole caused by [47:63] sign extension
ffff800000000000 - ffff87ffffffffff (=43 bits) guard hole, reserved for hypervisor
ffff880000000000 - ffffc7ffffffffff (=64 TB) direct mapping of all phys. memory
ffffc80000000000 - ffffc8ffffffffff (=40 bits) hole
ffffc90000000000 - ffffe8ffffffffff (=45 bits) vmalloc/ioremap space
ffffe90000000000 - ffffe9ffffffffff (=40 bits) hole
ffffea0000000000 - ffffeaffffffffff (=40 bits) virtual memory map (1TB)
... unused hole ...
ffffec0000000000 - fffffbffffffffff (=44 bits) kasan shadow memory (16TB)
... unused hole ...

                vaddr_end for KASLR

fffffe0000000000 - fffffe7fffffffff (=39 bits) cpu_entry_area mapping
fffffe8000000000 - fffffeffffffffff (=39 bits) LDT remap for PTI
ffffff0000000000 - ffffff7fffffffff (=39 bits) %esp fixup stacks
... unused hole ...
ffffffef00000000 - fffffffeffffffff (=64 GB) EFI region mapping space
... unused hole ...
ffffffff80000000 - ffffffff9fffffff (=512 MB)  kernel text mapping, from phys 0
ffffffffa0000000 - fffffffffeffffff (1520 MB) module mapping space
[fixmap start]   - ffffffffff5fffff kernel-internal fixmap range
ffffffffff600000 - ffffffffff600fff (=4 kB) legacy vsyscall ABI
ffffffffffe00000 - ffffffffffffffff (=2 MB) unused hole

http://www.kernel.org/doc/Doc...

剩下的是用户内存空间:

  • stack栈区:专门用来实现函数调用-栈结构的内存块。相对空间下(可以设置大小,Linux 一般默认是8M,可通过 ulimit –s 查看),系统自动管理,从高地址往低地址,向下生长。
  • 内存映射区:包括文件映射和匿名内存映射, 应用程序的所依赖的动态库,会在程序执行时候,加载到内存这个区域,一般包括数据(data)和代码(text);通过mmap系统调用,可以把特定的文件映射到内存中,然后在相应的内存区域中操作字节来访问文件内容,实现更高效的IO操作;匿名映射,在glibc中malloc分配大内存的时候会用到匿名映射。这里所谓的“大”表示是超过了MMAP_THRESHOLD 设置的字节数,它的缺省值是 128 kB,可以通过 mallopt() 去调整这个设置值。还可以用于进程间通信IPC(共享内存)。
  • heap堆区:主要用于用户动态内存分配,空间大,使用灵活,但需要用户自己管理,通过brk系统调用控制堆的生长,向高地址生长。
  • BBS段和DATA段:用于存放程序全局数据和静态数据,一般未初始化的放在BSS段(统一初始化为0,不占程序文件的空间),初始化的放在data段,只读数据放在rodata段(常量存储区)。
  • text段:主要存放程序二进制代码。

2  为了防止内存被攻击,比如栈溢出攻击和堆溢出攻击等,Linux在特定段之间使用随机偏移,使段的起始地址是随机值, Linux 系统上的ASLR 等级可以通过文件 /proc/sys/kernel/randomize_va_space 来进行设置,它支持以下取值:

  • 0 - 关闭的随机化。一切都是静止的。
  • 1 - 保守的随机化。共享库、栈、mmap()、VDSO以及堆将被随机化。
  • 2 - 完全的随机化。除了上面列举的要素外,通过 brk() 分配得到的内存空间也将被随机化。

3  每个段都有特定的安全控制(权限):

vm_flags

第三列,如r-xp

此段虚拟地址空间的属性。每种属性用一个字段表示,r表示可读,w表示可写,x表示可执行,p和s共用一个字段,互斥关系,p表示私有段,s表示共享段,如果没有相应权限,则用’-’代替

4 Linux虚拟内存是按页分配,每页大小为4KB或者2M,1G等(大页内存), 默认是4K;

5 例子-通过pmap 查看程序内存布局(综合proc/x/maps与proc/x/smaps数据):

#include<iostream>
#include <unistd.h>
using namespace std;
//long  a[1024*1024] = {0};
int main()
{
 void *heap;
 int *x = new int[1024]();
 cout << hex <<"x: " << x <<endl;
 heap = sbrk(0);
 //cout << hex << "a:" << (long) &a <<endl;
 cout << hex << "heap: " << (long) heap <<endl;
 cout << hex << "heap: " << (long)heap - (long)x <<endl;
 while(1);
 return 0;
 }
 g++  -g  -std=c++11 -o main  mem.cpp
./main

关闭内存地址随机化

pmap -X 8117
8117:   ./main
 Address Perm   Offset Device    Inode  Size  Rss Pss Referenced Anonymous Swap Locked Mapping
 00400000 r-xp 00000000  08:11 43014235     4    4   4          4         0    0      0 main
 00601000 r--p 00001000  08:11 43014235     4    4   4          4         4    0      0 main
 00602000 rw-p 00002000  08:11 43014235     4    4   4          4         4    0      0 main
 //程序的text段,只读数据段,和全局/静态数据段;
​
 00603000 rw-p 00000000  00:00        0   136    8   8          8         8    0      0 [heap]
 //程序的堆内存段;
​
 7ffff71e2000 r-xp 00000000  08:11   266401    88   88  18         88         0    0      0 libgcc_s.so.1
 7ffff71f8000 ---p 00016000  08:11   266401  2044    0   0          0         0    0      0 libgcc_s.so.1
 7ffff73f7000 rw-p 00015000  08:11   266401     4    4   4          4         4    0      0 libgcc_s.so.1
 7ffff73f8000 r-xp 00000000  08:11   266431  1052  224   3        224         0    0      0 libm-2.21.so
 7ffff74ff000 ---p 00107000  08:11   266431  2044    0   0          0         0    0      0 libm-2.21.so
 7ffff76fe000 r--p 00106000  08:11   266431     4    4   4          4         4    0      0 libm-2.21.so
 7ffff76ff000 rw-p 00107000  08:11   266431     4    4   4          4         4    0      0 libm-2.21.so
 7ffff7700000 r-xp 00000000  08:11   266372  1792 1152   8       1152         0    0      0 libc-2.21.so
 7ffff78c0000 ---p 001c0000  08:11   266372  2048    0   0          0         0    0      0 libc-2.21.so
 7ffff7ac0000 r--p 001c0000  08:11   266372    16   16  16         16        16    0      0 libc-2.21.so
 7ffff7ac4000 rw-p 001c4000  08:11   266372     8    8   8          8         8    0      0 libc-2.21.so
 7ffff7ac6000 rw-p 00000000  00:00        0    16   12  12         12        12    0      0
 7ffff7aca000 r-xp 00000000  08:11 46146360   960  856 283        856         0    0      0 libstdc++.so.6.0.20
 7ffff7bba000 ---p 000f0000  08:11 46146360  2048    0   0          0         0    0      0 libstdc++.so.6.0.20
 7ffff7dba000 r--p 000f0000  08:11 46146360    32   32  32         32        32    0      0 libstdc++.so.6.0.20
 7ffff7dc2000 rw-p 000f8000  08:11 46146360     8    8   8          8         8    0      0 libstdc++.so.6.0.20
 7ffff7dc4000 rw-p 00000000  00:00        0    84   16  16         16        16    0      0
 7ffff7dd9000 r-xp 00000000  08:11   266344   144  144   1        144         0    0      0 ld-2.21.so
//程序的内存映射区,主要是动态库加载到该内存区,包括动态库的text代码段和数据data段。
//中间没有名字的,属于程序的匿名映射段,主要提供大内存分配。
 7ffff7fd4000 rw-p 00000000  00:00        0    20   20  20         20        20    0      0
 7ffff7ff5000 rw-p 00000000  00:00        0    12   12  12         12        12    0      0
 7ffff7ff8000 r--p 00000000  00:00        0     8    0   0          0         0    0      0 [vvar]
 7ffff7ffa000 r-xp 00000000  00:00        0     8    4   0          4         0    0      0 [vdso]
//vvar page,kernel的一些系统调用的数据会映射到这个页面,用户可以直接在用户空间访问;
//vDSO -virtual dynamic shared object,is a small shared library exported by the kernel to accelerate the execution of certain system calls that do not necessarily have to run in kernel space, 就是内核实现了glibc的一些系统调用,然后可以直接在用户空间执行,提高系统调用效率和减少与glibc的耦合。
7ffff7ffc000 r--p 00023000  08:11   266344     4    4   4          4         4    0      0 ld-2.21.so
7ffff7ffd000 rw-p 00024000  08:11   266344     4    4   4          4         4    0      0 ld-2.21.so
7ffff7ffe000 rw-p 00000000  00:00        0     4    4   4          4         4    0      0
7ffffffde000 rw-p 00000000  00:00        0   136    8   8          8         8    0      0 [stack]
//此段为程序的栈区
​
ffffffffff600000 r-xp 00000000  00:00        0     4    0   0          0         0    0      0 [vsyscall]
//此段是Linux实现vsyscall系统调用vsyscall库代码段
 ===== ==== === ========== ========= ==== ======
 12744 2644 489       2644       172    0      0 KB

思考问题:

1  栈为什么要由高地址向低地址扩展,堆为什么由低地址向高地址扩展?

  • 历史原因:在没有MMU的时代,为了最大的利用内存空间,堆和栈被设计为从两端相向生长。那么哪一个向上,哪一个向下呢?人们对数据访问是习惯于向上的,比如你在堆中new一个数组,是习惯于把低元素放到低地址,把高位放到高地址,所以堆 向上生长比较符合习惯,  而栈则对方向不敏感,一般对栈的操作只有PUSH和pop,无所谓向上向下,所以就把堆放在了低端,把栈放在了高端. 但现在已经习惯这样了。这个和处理器设计有关系,目前大多数主流处理器都是这样设计,但ARM 同时支持这两种增长方式。

2  如何查看进程虚拟地址空间的使用情况?

3  对比堆和栈优缺点?

四  C++栈内存空间模型

  1. C++程序运行调用栈示意图:

    函数调用过程中,栈(有俗称堆栈)的变化:

      from  https://zhuanlan.zhihu.com/p/...

  1. 当主函数调用子函数的时候:
  • 在主函数中,将子函数的参数按照一定调用约定(参考调用约定),一般是从右向左把参数push到栈中;
  • 然后把下一条指令地址,即返回地址(return address)push入栈(隐藏在call指令中);
  • 然后跳转到子函数地址处执行:call 子函数;此时
   2.  子函数执行:
  • push  %rbp  :  把当前rbp的值保持在栈中;
  • mov %rsp, %rbp:把rbp移到最新栈顶位置,即开启子函数的新帧;
  • [可选]sub $xxx, %esp: 在栈上分配XXX字节的临时空间。(抬高栈顶)(编译器根据函数中的局部变量的总大小确定临时空间的大小);
  • [可选]push XXX:    保存(push)一些寄存器的值;
    3.  子函数调用返回:
  • 保持返回值:一般将函数函数值保持在eax寄存器中;
  • [可选]恢复(pop)一些寄存器的值;
  • mov %rbp,%rsp: 收回栈空间,恢复主函数的栈顶;
  • pop %rbp;恢复主函数的栈底;

    在AT&T中:

    以上两条指令可以被leave指令取代

  • leave
  • ret;从栈顶获取之前保持的返回地址(return address),并跳转到此位置执行;

栈攻击

由上面栈内存布局可以看出,栈很容易被破坏和攻击,通过栈缓冲器溢出攻击,用攻击代码首地址来替换函数帧的返回地址,当子函数返回时,便跳转到攻击代码处执行,获取系统的控制权,所以操作系统和编译器采用了一些常用的防攻击的方法:

  • ASLR(地址空间布局随机化):操作系统可以将函数调用栈的起始地址设为随机化(这种技术被称为内存布局随机化,即Address Space Layout Randomization (ASLR) ),加大了查找函数地址及返回地址的难度。
  • Cannary

  gcc关于栈溢出检测的几个参数:

开启Canary之后,函数开始时在ebp和临时变量之间插入一个随机值,函数结束时验证这个值。如果不相等(也就是这个值被其他值覆盖了),就会调用 _stackchk_fail函数,终止进程。对应GCC编译选项-fno-stack-protector解除该保护。

  • NX.
    开启NX保护之后,程序的堆栈将会不可执行。对应GCC编译选项-z execstack解除该保护。

栈异常处理

  • 一个函数(或方法)抛出异常,那么它首先将当前栈上的变量全部清空(unwinding),如果变量是类对象的话,将调用其析构函数,接着,异常来到call stack的上一层,做相同操作,直到遇到catch语句。
  • 指针是一个普通的变量,不是类对象,所以在清空call stack时,指针指向资源的析构函数将不会调用。

思考问题:

1  递归调用函数怎么从20层直接返回到17层,程序可以正常运行?

参考上面栈帧的结构,中心思想是当递归函数执行到第20层的时候,把当前栈帧的rbp值替换为17层的rbp的值,  怎么得到17层rbp的值, 就是通过反复取rbp的值(rbp保持了上一帧的rbp),核心代码如下:

/*change stack*/
int ret_stack(int layer)
{
 unsigned long rbp = 0;
 unsigned long layer_rbp = 0;
 int depth = 0;
 
 /* 1.得到首层函数的栈基址 */
 __asm__ volatile(
 "movq %%rbp, %0 nt"
 :"=r"(rbp)
 :
 :"memory");
 layer_rbp = rbp;
 cout << hex<< rbp <<endl;
 /* 2.逐层回溯栈基址 */
 for(; (depth < layer) && (0 != layer_rbp) && (0 != *(unsigned long *)layer_rbp) && (layer_rbp != *(unsigned long *)layer_rbp); ++depth) {
 cout << hex<< layer_rbp <<endl;
 layer_rbp = *(unsigned long *)layer_rbp;
 }
 cout << hex<< layer_rbp <<endl;
 //change current rbp to target layer rbp
 unsigned long *x = (unsigned long *)rbp;
 *x = layer_rbp;
 cout << hex<< x << " v:" << *x <<endl;
 return depth;
 }
​

2  调用约定有哪些?

我们最常用是以下几种约定

  1. cdec

‍是c/c++默认的调用约定

  1. stdcall

它是微软Win32 API的一准标准,我们常用的回调函数就是通过这种调用方式

  1. thiscall

thiscall 是c++中非静态类成员函数的默认调用约定

五 C++堆内存空间模型

1. C++ 程序动态申请内存new/delete

new/delete 操作符,C++内置操作符

  1. new操作符做两件事,分配内存+调用构造函数初始化。你不能改变它的行为;
  2. delete操作符同样做两件事,调用析构函数+释放内存。你不能改变它的行为;
operator new/delete 函数

operator new :

The default _allocation and deallocation functions_ are special components of the standard library; They have the following unique properties:

  • Global: All three versions of operator new are declared in the global namespace, not within thestdnamespace.
  • Implicit: The allocating versions (_(1)_ and _(2)_) are _implicitly declared_ in every translation unit of a C++ program, no matter whether header <new> is included or not.
  • Replaceable: The allocating versions (_(1)_ and _(2)_) are also _replaceable_: A program may provide its own definition that replaces the one provided by default to produce the result described above, or can overload it for specific types.

If set_new_handler has been used to define anew_handler function, this _new-handler_ function is called by the default definitions of the allocating versions (_(1)_ and _(2)_) if they fail to allocate the requested storage.
operator new can be called explicitly as a regular function, but in C++, new is an operator with a very specific behavior: An expression with the new operator, first calls function operator new (i.e., this function) with the size of its type specifier as first argument, and if this is successful, it then automatically initializes or constructs the object (if needed). Finally, the expression evaluates as a pointer to the appropriate type.

from http://www.cplusplus.com/refe...

1.是用来专门分配内存的函数,为new操作符调用,你能增加额外的参数重载函数operator new(有限制):

限制1:第一个参数类型必须是size_t;

限制2:函数必须返回void*;

2.operator new 底层一般调用malloc函数(gcc+glibc)分配内存;

3.operator new 分配失败会抛异常(默认),通过传递参数也可以不抛异常,返回空指针;

operator delete :

1.是用来专门分配内存的函数,为delete操作符调用,你能增加额外的参数重载函数operator delete(有限制):

限制1:第一个参数类型必须是void*;

限制2:函数必须返回void;

2.operator delete底层一般调用free函数(gcc+glibc)释放内存;

3.operator delete分配失败会抛异常(默认),通过传递参数也可以不抛异常,返回空指针;

placement new/delete 函数
  1. placement new 其实就是new的一种重载,placement new是一种特殊的operator new,作用于一块已分配但未处理或未初始化的raw内存,就是用一块已经分配好的内存上重建对象(调用构造函数);
  2. 它是C++库标准的一部分;
  3. placement delete 什么都不做;
4. 数组分配 new[]/delete[] 表达式
  • 对应会调用operator new[]/delete[]函数;
  • 按对象的个数,分别调用构造函数和析构函数;

http://www.cplusplus.com/refe...[]/

class-specific allocation functions(成员函数)

http://en.cppreference.com/w/...

定制对象特殊new/delete函数;

实现一般是使用全局:

::operator new

::operator delete

关键点:

  • 你想在堆上建立一个对象,应该用new操作符。它既分配内存又为对象调用构造函数。
  • 如果你仅仅想分配内存,就应该调用operator new函数;它不会调用构造函数。
  • 如果你想定制自己的在堆对象被建立时的内存分配过程,你应该写你自己的operator new函数,然后使用new操作符,new操作符会调用你定制的operator new。
  • 如果你想在一块已经获得指针的内存里建立一个对象,应该用placement new。
  • C++可以为分配失败设置自己的异常处理函数:
  If set_new_handler  has been used to define a new_handler function, this _new-handler_ function is called by the default definitions of the allocating versions (_(1)_ and _(2)_) if they fail to allocate the requested storage.
  • 如果在构造函数时候抛出异常,new表达式后面会调用对应operator delete函数释放内存:
The other signatures (_(2)_ and _(3)_) are never called by a _delete-expression_ (the delete operator always calls the ordinary version of this function, and exactly once for each of its arguments). These other signatures are only called automatically by a _new-expression_ when their object construction fails (e.g., if the constructor of an object throws while being constructed by a _new-expression_withnothrow, the matchingoperator deletefunction accepting anothrowargument is called).

思考问题:

1  malloc和free是怎么实现的?

2  malloc 分配多大的内存,就占用多大的物理内存空间吗?

3  free 的内存真的释放了吗(还给 OS ) ?

4  既然堆内内存不能直接释放,为什么不全部使用 mmap 来分配?

5  如何查看堆内内存的碎片情况?

6  除了 glibc 的 malloc/free ,还有其他第三方实现吗?

2.  C++11的智能指针与垃圾回收

  • C++智能指针出现是为了解决由于支持动态内存分配而导致的一些C++内存问题,比如内存泄漏,对象生命周期的管理,悬挂指针(dangling pointer)/空指针等问题;
  • C++智能指针通过RAII设计模式去管理对象生命周期(动态内存管理),提供带少量异常类似普通指针的操作接口,在对象构造的时候分配内存,在对象作用域之外释放内存,帮助程序员管理动态内存;
  • 老的智能指针auto_ptr由于设计语义不好而导致很多不合理问题:不支持复制(拷贝构造函数)和赋值(operator =),但复制或赋值的时候不会提示出错。因为不能被复制,所以不能被放入容器中。而被C++11弃用(deprecated);

新的智能指针:

1. shared_ptr

  • shared_ptr是引用计数型(reference counting)智能指针, shared_ptr包含两个成员,一个是指向真正数据的指针,另一个是引用计数ref_count模块指针,对比GCC实现,大致原理如下,

共享对象(数据)(赋值拷贝),引用计数加1,指针消亡,引用计数减1,当引用计数为0,自动`析构所指的对象,引用计数是线程安全的(原子操作)。`

shared_ptr关键点:

  • 用shared_ptr就不要new,保证内存管理的一致性;
  • 使用weak_ptr来打破循环引用;
  • 用make_shared来生成shared_ptr,提高效率,内存分配一次搞定,防止异常导致内存泄漏,参考https://herbsutter.com/gotw/_102/;
  • `大量的shared_ptr会导致程序性能下降(相对其他指针),需要等到所有的weak引用为0时才能最终释放内存(delete);
    `
  • 用enable_shared_from_this来使一个类能获取自身的shared_ptr;
  • 不能在对象的构造函数中使用shared_from_this()函数,因为对象还没有构造完毕,share_ptr还没有初始化构造完全;构造顺序:先需要调用enable_shared_from_this类的构造函数,接着调用对象的构造函数,最后需要调用shared_ptr类的构造函数初始化enable_shared_from_this的成员变量weak_this_。然后才能使用shared_from_this()函数;

2. unique_ptr

独占指针,不共享,不能赋值拷贝;

unique_ptr关键点:

1. 如果对象不需要共享,一般最好都用unique_ptr,性能好,更安全;

2. 可以通过move语义传递对象的生命周期控制权;

3. 函数可以返回unique_ptr对象,为什么?

RVO和NRVO

       当函数返回一个对象时,理论上会产生临时变量,那必然是会导致新对象的构造和旧对象的析构,这对效率是有影响的。C++编译针对这种情况允许进行优化,哪怕是构造函数有副作用,这叫做返回值优化(RVO),返回有名字的对象叫做具名返回值优化(NRVO),就那RVO来说吧,本来是在返回时要生成临时对象的,现在构造返回对象时直接在接受返回对象的空间中构造了。假设不进行返回值优化,那么上面返回unique_ptr会不会有问题呢?也不会。因为标准允许编译器这么做:

1.如果支持move构造,那么调用move构造。

2.如果不支持move,那就调用copy构造。

3.如果不支持copy,那就报错吧。

显然的,unique_ptr是支持move构造的,unique_ptr对象可以被函数返回。

3. weak_ptr

  • 引用对象,不增加引用计数,对象生命周期,无法干预;
  • 配合shared_ptr解决shared_ptr循环引用问题;
  • 可以影响到对象内存最终释放的时间;

更详细参考:

http://en.cppreference.com/w/...

思考问题:

1 C++的赋值和Java的有什么区别?

C++的赋值可以是对象拷贝也可以对象引用,java的赋值是对象引用;

2 smart_ptr有哪些坑可以仍然导致内存泄漏?

2.1.shared_ptr初始化构造函数指针,一般是可以动态管理的内存地址,如果不是就可能导致内存泄漏;

2.2.shared_ptr要求内部new和delete实现必须是成对,一致性,如果不是就可能导致内存泄漏;

2.3. shared_ptr对象和其他大多数STL容器一样,本身不是线程安全的,需要用户去保证;

3 unique_ptr有哪些限制?

  • 只能移动赋值转移数据,不能拷贝;
  • 不支持类型转换(cast);

4 智能指针是异常安全的吗?

所谓异常安全是指,当异常抛出时,带有异常安全的函数会:

  • 不泄露任何资源
  • 不允许数据被破坏

智能指针就是采用RAII技术,即以对象管理资源来防止资源泄漏。

Exception Safety

Several functions in these smart pointer classes are specified as having "no effect" or "no effect except such-and-such" if an exception is thrown. This means that when an exception is thrown by an object of one of these classes, the entire program state remains the same as it was prior to the function call which resulted in the exception being thrown. This amounts to a guarantee that there are no detectable side effects. Other functions never throw exceptions. The only exception ever thrown by functions which do throw (assuming T meets the  common requirements) is std::bad_alloc, and that is thrown only by functions which are explicitly documented as possibly throwing std::bad_alloc.

https://www.boost.org/doc/libs/1_61_0/libs/smart_ptr/smart_ptr.htm

5 智能指针是线程安全的吗?

智能指针对象的引用计数模块是线程安全的,因为 shared_ptr 有两个数据成员,读写操作不能原子化,所以对象本身不是线程安全的,需要用户去保证线程安全。

Thread Safety

shared_ptr objects offer the same level of thread safety as built-in types. A shared_ptr instance can be "read" (accessed using only const operations) simultaneously by multiple threads. Different shared_ptr instances can be "written to" (accessed using mutable operations such as operator= or reset) simultaneously by multiple threads (even when these instances are copies, and share the same reference count underneath.)

Any other simultaneous accesses result in undefined behavior.

https://www.boost.org/doc/lib...

C++标准垃圾回收

C++11 提供最小垃圾支持

declare_reachable
undeclare_reachable
declare_no_pointers
undeclare_no_pointers
pointer_safety
get_pointer_safety

由于很多场景受限,当前几乎没有人使用;

感兴趣可以参考:

http://www.stroustrup.com/C++...

http://www.openstd.org/jtc1/s...

思考问题:

1  C++可以通过哪些技术来支持“垃圾回收”?

smart_ptr,RAII, move语义等;

2  RAII是指什么?

RAII是指Resource Acquisition IInitialization的设计模式,

RAII要求,资源的有效期与持有资源的对象的生命期严格绑定,即由对象的构造函数完成资源的分配(获取),同时由析构函数完成资源的释放。在这种要求下,只要对象能正确地析构,就不会出现资源泄露问题。

当一个函数需要通过多个局部变量来管理资源时,RAII就显得非常好用。因为只有被构造成功(构造函数没有抛出异常)的对象才会在返回时调用析构函数,同时析构函数的调用顺序恰好是它们构造顺序的反序,这样既可以保证多个资源(对象)的正确释放,又能满足多个资源之间的依赖关系。

由于RAII可以极大地简化资源管理,并有效地保证程序的正确和代码的简洁,所以通常会强烈建议在C++中使用它。

from https://zh.wikipedia.org/wiki...

3.  C++ STL 内存模型

STL(C++标准模板库)引入的一个Allocator概念。整个STL所有组件的内存均从allocator分配。也就是说,STL并不推荐使用 new/delete 进行内存管理,而是推荐使用allocator。

SGI STL allocator总体设计

对象的构造和析构采用placement new函数:

内存配置:

 分配算法:

思考问题:

  1. vector内存设计和array的区别和适用的场景?
  2. 遍历map与遍历vector哪个更快,为什么?
  3. STL的map和unordered_map内存设计各有什么不同?

六  C++内存问题及常用的解决方法

1. 内存管理功能问题

由于C++语言对内存有主动控制权,内存使用灵活和效率高,但代价是不小心使用就会导致以下内存错误:

• memory overrun:写内存越界 
• double free:同一块内存释放两次 
• use after free:内存释放后使用 
• wild free:释放内存的参数为非法值 
• access uninitialized memory:访问未初始化内存 
• read invalid memory:读取非法内存,本质上也属于内存越界 
• memory leak:内存泄露 
• use after return:caller访问一个指针,该指针指向callee的栈内内存 
• stack overflow:栈溢出

常用的解决内存错误的方法

  • 代码静态检测

静态代码检测是指无需运行被测代码,通过词法分析、语法分析、控制流、数据流分析等技术对程序代码进行扫描,找出代码隐藏的错误和缺陷,如参数不匹配,有歧义的嵌套语句,错误的递归,非法计算,可能出现的空指针引用等等。统计证明,在整个软件开发生命周期中,30%至70%的代码逻辑设计和编码缺陷是可以通过静态代码分析来发现和修复的。在C++项目开发过程中,因为其为编译执行语言,语言规则要求较高,开发团队往往要花费大量的时间和精力发现并修改代码缺陷。所以C++静态代码分析工具能够帮助开发人员快速、有效的定位代码缺陷并及时纠正这些问题,从而极大地提高软件可靠性并节省开发成本。

静态代码分析工具的优势:

1、自动执行静态代码分析,快速定位代码隐藏错误和缺陷。

2、帮助代码设计人员更专注于分析和解决代码设计缺陷。

3、减少在代码人工检查上花费的时间,提高软件可靠性并节省开发成本。

一些主流的静态代码检测工具,免费的cppcheck,clang static analyzer;

商用的coverity,pclint等

 各个工具性能对比: 

  http://www.51testing.com/html...

  • 代码动态检测

所谓的代码动态检测,就是需要再程序运行情况下,通过插入特殊指令,进行动态检测和收集运行数据信息,然后分析给出报告。

1.为了检测内存非法使用,需要hook内存分配和操作函数。hook的方法可以是用C-preprocessor,也可以是在链接库中直接定义(因为Glibc中的malloc/free等函数都是weak symbol),或是用LD_PRELOAD。另外,通过hook strcpy(),memmove()等函数可以检测它们是否引起buffer overflow。

  1. 为了检查内存的非法访问,需要对程序的内存进行bookkeeping,然后截获每次访存操作并检测是否合法。bookkeeping的方法大同小异,主要思想是用shadow memory来验证某块内存的合法性。至于instrumentation的方法各种各样。有run-time的,比如通过把程序运行在虚拟机中或是通过binary translator来运行;或是compile-time的,在编译时就在访存指令时就加入检查操作。另外也可以通过在分配内存前后加设为不可访问的guard page,这样可以利用硬件(MMU)来触发SIGSEGV,从而提高速度。

3.为了检测栈的问题,一般在stack上设置canary,即在函数调用时在栈上写magic number或是随机值,然后在函数返回时检查是否被改写。另外可以通过mprotect()在stack的顶端设置guard page,这样栈溢出会导致SIGSEGV而不至于破坏数据。

工具总结对比,常用valgrind(检测内存泄露),gperftools(统计内存消耗)等:

image.png
DBI:动态二进制工具
CTI:编译时工具
UMR:未初始化的存储器读取
UAF:释放后使用(又名悬挂指针)
UAR:返回后使用
OOB:越界
x86:包括32和64-少量。在GCC 4.9中已删除了
Mudflap,因为它已被AddressSanitizer取代。
Guard Page:一系列内存错误检测器(Linux上为电子围栏或DUMA,Windows上为Page Heap,OS X上为
libgmallocgperftools:与TCMalloc捆绑在一起的各种性能工具/错误检测器。堆检查器(检漏器)仅在Linux上可用。调试分配器同时提供了保护页和Canary值,以更精确地检测OOB写入,因此它比仅保护页的检测器要好。

https://github.com/google/san...

2. C++内存管理效率问题

  1. 内存管理可以分为三个层次

    自底向上分别是:
  • 第一层:操作系统内核的内存管理-虚拟内存管理
  • 第二层:glibc层维护的内存管理算法
  • 第三层:应用程序从glibc动态分配内存后,根据应用程序本身的程序特性进行优化, 比如SGI STL allocator,使用引用计数std::shared_ptr,RAII,实现应用的内存池等等。

当然应用程序也可以直接使用系统调用从内核分配内存,自己根据程序特性来维护内存,但是会大大增加开发成本。

      2.  C++内存管理问题
  • 频繁的new/delete势必会造成内存碎片化,使内存再分配和回收的效率下降;
  • new/delete分配内存在linux下默认是通过调用glibc的api-malloc/free来实现的,而这些api是通过调用到linux的系统调用:

brk()/sbrk() // 通过移动Heap堆顶指针brk,达到增加内存目的
mmap()/munmap() // 通过文件影射的方式,把文件映射到mmap区

  • 分配内存 < DEFAULT_MMAP_THRESHOLD,走brk,从内存池获取,失败的话走brk系统调用
  • 分配内存 > DEFAULT_MMAP_THRESHOLD,走mmap,直接调用mmap系统调用

其中,DEFAULT_MMAP_THRESHOLD默认为128k,可通过mallopt进行设置。

sbrk/brk系统调用的实现:分配内存是通过调节堆顶的位置来实现, 堆顶的位置是通过函数 brk 和 sbrk 进行动态调整,参考例子:

(1) 初始状态:如图 (1) 所示,系统已分配 ABCD 四块内存,其中 ABD 在堆内分配, C 使用 mmap 分配。为简单起见,图中忽略了如共享库等文件映射区域的地址空间。

(2) E=malloc(100k) :分配 100k 内存,小于 128k ,从堆内分配,堆内剩余空间不足,扩展堆顶 (brk) 指针。

(3) free(A) :释放 A 的内存,在 glibc 中,仅仅是标记为可用,形成一个内存空洞 ( 碎片 ),并没有真正释放。如果此时需要分配 40k 以内的空间,可重用此空间,剩余空间形成新的小碎片。

(4) free(C) :C 空间大于 128K ,使用 mmap 分配,如果释放 C ,会调用 munmap 系统调用来释放,并会真正释放该空间,还给 OS ,如图 (4) 所示。

           

所以free的内存不一定真正的归还给OS,随着系统频繁地 malloc 和 free ,尤其对于小块内存,堆内将产生越来越多不可用的碎片,导致“内存泄露”。而这种“泄露”现象使用 valgrind 是无法检测出来的。

             

  • 综上,频繁内存分配释放还会导致大量系统调用开销,影响效率,降低整体性能;
3. 常用解决上述问题的方案

内存池技术

内存池方案通常一次从系统申请一大块内存块,然后基于在这块内存块可以进行不同内存策略实现,可以比较好得解决上面提到的问题,一般采用内存池有以下好处:

1.少量系统申请次数,非常少(几没有) 堆碎片。
       2.由于没有系统调用等,比通常的内存申请/释放(比如通过malloc, new等)的方式快。
       3.可以检查应用的任何一块内存是否在内存池里。
       4.写一个”堆转储(Heap-Dump)”到你的硬盘(对事后的调试非常有用)。
       5.可以更方便实现某种内存泄漏检测(memory-leak detection)。

6.减少额外系统内存管理开销,可以节约内存;

内存管理方案实现的指标:

  • 额外的空间损耗尽量少
  • 分配速度尽可能快
  • 尽量避免内存碎片
  • 多线程性能好
  • 缓存本地化友好
  • 通用性,兼容性,可移植性,易调试等

各个内存分配器的实现都是在以上的各种指标中进行权衡选择.

4.  一些业界主流的内存管理方案

SGI STL allocator

是比较优秀的 C++库内存分配器(细节参考上面描述)

ptmalloc

 是glibc的内存分配管理模块, 主要核心技术点:

  1. Arena-main /thread;支持多线程
  2. Heap segments;for thread arena via by mmap call ;提高管理
  3. chunk/Top chunk/Last Remainder chunk;提高内存分配的局部性
  4. bins/fast bin/unsorted bin/small bin/large bin;提高分配效率

ptmalloc的缺陷

  • 后分配的内存先释放,因为 ptmalloc 收缩内存是从 top chunk 开始,如果与 top chunk 相邻的 chunk 不能释放, top chunk 以下的 chunk 都无法释放。
  • 多线程锁开销大, 需要避免多线程频繁分配释放。
  • 内存从thread的areana中分配, 内存不能从一个arena移动到另一个arena, 就是说如果多线程使用内存不均衡,容易导致内存的浪费。比如说线程1使用了300M内存,完成任务后glibc没有释放给操作系统,线程2开始创建了一个新的arena, 但是线程1的300M却不能用了。
  • 每个chunk至少8字节的开销很大
  • 不定期分配长生命周期的内存容易造成内存碎片,不利于回收。64位系统最好分配32M以上内存,这是使用mmap的阈值。

tcmalloc

google的gperftools内存分配管理模块, 主要核心技术点:

          

  1. thread-localcache/periodic garbagecollections/CentralFreeList;提高多线程性能,提高cache利用率

    TCMalloc给每个线程分配了一个线程局部缓存。小分配可以直接由线程局部缓存来满足。需要的话,会将对象从中央数据结构移动到线程局部缓存中,同时定期的垃圾收集将用于把内存从线程局部缓存迁移回中央数据结构中:

         

     2.  Thread Specific Free List/size-classes [8,16,32,…32k]: 更好小对象内存分配;

每个小对象的大小都会被映射到170个可分配的尺寸类别中的一个。例如,在分配961到1024字节时,都会归整为1024字节。尺寸类别这样隔开:较小的尺寸相差8字节,较大的尺寸相差16字节,再大一点的尺寸差32字节,如此类推。最大的间隔(对于尺寸 >= ~2K的)是256字节。一个线程缓存对每个尺寸类都包含了一个自由对象的单向链表

        

    3.  The central page heap:更好的大对象内存分配,一个大对象的尺寸(> 32K)会被除以一个页面尺寸(4K)并取整(大于结果的最小整数),同时是由中央页面堆来处理   的。中央页面堆又是一个自由列表的阵列。对于i < 256而言,第k个条目是一个由k个页面组成的自由列表。第256个条目则是一个包含了长度>= 256个页面的自由列表:

     

   4.  Spans:

TCMalloc管理的堆由一系列页面组成。连续的页面由一个“跨度”(Span)对象来表示。一个跨度可以是_已被分配_或者是_自由_的。如果是自由的,跨度则会是一个页面堆链表中的一个条目。如果已被分配,它会是一个已经被传递给应用程序的大对象,或者是一个已经被分割成一系列小对象的一个页面。如果是被分割成小对象的,对象的尺寸类别会被记录在跨度中。

由页面号索引的中央数组可以用于找到某个页面所属的跨度。例如,下面的跨度_a_占据了2个页面,跨度_b_占据了1个页面,跨度_c_占据了5个页面最后跨度_d_占据了3个页面。

tcmalloc的改进

  • ThreadCache会阶段性的回收内存到CentralCache里。解决了ptmalloc2中arena之间不能迁移的问题。
  • Tcmalloc占用更少的额外空间。例如,分配N个8字节对象可能要使用大约8N * 1.01字节的空间。即,多用百分之一的空间。Ptmalloc2使用最少8字节描述一个chunk。
  • 更快。小对象几乎无锁, >32KB的对象从CentralCache中分配使用自旋锁。并且>32KB对象都是页面对齐分配,多线程的时候应尽量避免频繁分配,否则也会造成自旋锁的竞争和页面对齐造成的浪费。

jemalloc

FreeBSD的提供的内存分配管理模块, 主要核心技术点:

1. 与tcmalloc类似,每个线程同样在<32KB的时候无锁使用线程本地cache;

  1. Jemalloc在64bits系统上使用下面的size-class分类:

Small: [8], [16, 32, 48, …, 128], [192, 256, 320, …, 512], [768, 1024, 1280, …, 3840]
Large: [4 KiB, 8 KiB, 12 KiB, …, 4072 KiB]
Huge: [4 MiB, 8 MiB, 12 MiB, …]

  1. small/large对象查找metadata需要常量时间, huge对象通过全局红黑树在对数时间内查找
  2. 虚拟内存被逻辑上分割成chunks(默认是4MB,1024个4k页),应用线程通过round-robin算法在第一次malloc的时候分配arena, 每个arena都是相互独立的,维护自己的chunks, chunk切割pages到small/large对象。free()的内存总是返回到所属的arena中,而不管是哪个线程调用free().

  

上图可以看到每个arena管理的arena chunk结构, 开始的header主要是维护了一个page map(1024个页面关联的对象状态), header下方就是它的页面空间。Small对象被分到一起, metadata信息存放在起始位置。large chunk相互独立,它的metadata信息存放在chunk header map中。

  1. 通过arena分配的时候需要对arena bin(每个small size-class一个,细粒度)加锁,或arena本身加锁。并且线程cache对象也会通过垃圾回收指数退让算法返回到arena中。

jemalloc的优化

  • Jmalloc小对象也根据size-class,但是它使用了低地址优先的策略,来降低内存碎片化。
  • Jemalloc大概需要2%的额外开销。(tcmalloc 1%, ptmalloc最少8B).
  • Jemalloc和tcmalloc类似的线程本地缓存,避免锁的竞争 .
  • 相对未使用的页面,优先使用dirty page,提升缓存命中。

性能比较

测试环境:2x Intel E5/2.2Ghz with 8 real cores per socket,16 real cores, 开启hyper-threading, 总共32个vcpu。16个table,每个5M row。OLTP_RO测试包含5个select查询:select_ranges, select_order_ranges, select_distinct_ranges, select_sum_ranges:

facebook的测试结果:

服务器吞吐量分别用6个malloc实现的对比数据,可以看到tcmalloc和jemalloc最好(tcmalloc这里版本较旧)。

详细参考:

https://www.facebook.com/note...

总结

可以看出tcmalloc和jemalloc性能接近,比ptmalloc性能要好,在多线程环境使用tcmalloc和jemalloc效果非常明显。一般支持多核多线程扩展情况下可以使用jemalloc;反之使用tcmalloc可能是更好的选择。

可以参考:

https://sploitfun.wordpress.c...

http://goog-perftools.sourcef...

https://www.facebook.com/note...

https://blog.csdn.net/junlon2...

思考问题:

1  jemalloc和tcmalloc最佳实践是什么?

2  内存池的设计有哪些套路?为什么?

七  C++程序内存性能测试

  1. 用系统工具抓取性能数据

    ###

  • pmap

    通过读取/proc/$PID/maps 和 smaps 的数据,解析数据,生成进程的虚列内存映像和一些内存统计:

 pmap -X -p 31931
31931:   ./bug_tc
Address Perm   Offset Device    Inode  Size   Rss   Pss Referenced Anonymous Swap Locked Mapping
 …
7f37e4c36000 rw-p 00000000  00:00        0    132     88     88         80        88     44      0 [heap]
7fffff85c000 rw-p 00000000  00:00        0  7824  7820  7820       7820      7820    0      0 [stack]
 … 
​
 ===== ===== ===== ========== ========= ==== ======
 71396 16540 13902      16540     13048    0      0 KB

里面可以查看程序堆和栈内存大小区间,程序所占内存大小,主要是关注PSS

以下内存统计名称解释:

VSS:Virtual Set Size,虚拟内存耗用内存,包括共享库的内存;

RSS:Resident Set Size,实际使用物理内存,包括共享库;

PSS:Proportional Set Size,实际使用的物理内存,共享库按比例分配;

USS:Unique Set Size,进程独占的物理内存,不计算共享库,也可以理解为将进程杀 死能释放出的内存;

一般VSS >= RSS >= PSS >= USS, 一般统计程序的内存占用,PSS是最好的选择,比较合理。

  • top

实时显示内存当前使用情况和各个进程使用内存信息

  • free 

查看系统可用内存和占用情况

  • /proc/meminfo

 查看机器使用内存使用统计和内存硬件基本信息。

  • vmstat

    监控内存变化

详细请参考man手册:

http://linuxtools-rst.readthe...

思考问题:

1  各个工具优缺点和使用场景?

2   linux内存统计里面,划分了哪些统计?

参加答案

2. valgrind  massif

堆栈分析器,指示程序中使用了多少堆内存等信息,可以帮助你减少程序内存使用量,因为更小程序更能多占cache,减少分页,加速程序;对于需要大量内存的程序,可以让程序能够减少交换分区使用,加速程序。

valgrind massif 采集完数据生成数据文件,数据文件会显示每一帧的程序使用的堆内存大小,

The Snapshot Details 显示更多细节:

更多细节参考:

http://valgrind.org/docs/manu...

3. gperftools--heap profile

gperftools工具里面的内存监控器,统计监控程序使用内存的多少,可以查看内存使用热点,默认是100ms一次采样。

text模式:% pprof --text test_tc  test.prof

Total: 38 samples
       7  18.4%  18.4%        7  18.4% operator delete[] (inline)
       3   7.9%  26.3%        3   7.9% PackedCache::TryGet (inline)
       3   7.9%  34.2%       37  97.4% main::{lambda#1}::operator
       3   7.9%  42.1%        5  13.2% operator new (inline)
       3   7.9%  50.0%        4  10.5% tcmalloc::CentralFreeList::ReleaseToSpans
       2   5.3%  55.3%        2   5.3% SpinLock::SpinLoop
       2   5.3%  60.5%        2   5.3% _init
       2   5.3%  65.8%        2   5.3% tcmalloc::CentralFreeList::FetchFromOneSpans
       2   5.3%  71.1%        2   5.3% tcmalloc::ThreadCache::GetThreadHeap (inline)
       2   5.3%  76.3%        2   5.3% tcmalloc::ThreadCache::ReleaseToCentralCache (inline)
       1   2.6%  78.9%        1   2.6% ProfileData::FlushTable
       1   2.6%  81.6%        4  10.5% SpinLock::Lock (inline)
       1   2.6%  84.2%        1   2.6% TCMalloc_PageMap2::get (inline)
       1   2.6%  86.8%        5  13.2% tcmalloc::CentralFreeList::ReleaseListToSpans
       1   2.6%  89.5%        6  15.8% tcmalloc::CentralFreeList::RemoveRange
       1   2.6%  92.1%        1   2.6% tcmalloc::SizeMap::GetSizeClass (inline)

  • 第一列代表这个函数调用本身直接使用了多少内存,
  • 第二列表示第一列的百分比,
  • 第三列是从第一行到当前行的所有第二列之和,
  • 第四列表示这个函数调用自己直接使用加上所有子调用使用的内存总和,
  • 第五列是第四列的百分比。

基本上只要知道这些,就能很好的掌握每一时刻程序运行内存使用情况了,并且对比不同时段的不同profile数据,可以分析出内存走向,进而定位热点和泄漏。

pdf模式:可以把采样的结果转换为图模式,这样查看更为直观:

   

Kcachegrind模式:利用pprof生成callgrind格式的文件即可,KCachegrind的GUI工具,用于分析callgrind

  • 图形化地浏览源码和执行次数,并使用各种排序来搜索可优化的东西。
  • 分析不同的图表,来可视化地观察什么占据了大多数时间,以及它调用了什么。
  • 查看真实的汇编机器码输出,使你能够看到实际的指令,给你更多的线索。
  • 可视化地显示源码中的循环和分支的跳跃方式,便于你更容易地找到优化代码的方法。

更多细节参考

https://github.com/gperftools...

windows 版本:

https://sourceforge.net/proje...

思考问题:

1  说一说内存对设备(手机,PC,嵌入式设备)性能影响?

参考:

https://blog.csdn.net/yang_yu...

https://blog.csdn.net/buxizhi...

http://www.cnblogs.com/heleif...

https://herbsutter.com/gotw/_...

https://lanzkron.wordpress.co...

http://en.cppreference.com/w/...

欢迎微信搜索「职场重生」,关注公众号「职场重生」,后续更多精彩内容发布;

想入群同学,可以关注公众号回复"入群",可以结识一帮志同道合,对技术有追求的朋友,大厂工作内推(简历,职位信息,面试经验等,会尽最大努力提供帮助),职场经验分享,技术深化讨论,如何运动健身等。

Docker入门,看这篇就够了

Docker入门,看这篇就够了

Docker是怎么出现的

关于Docker的发展史,本文就不做介绍,有兴趣的小伙伴们可以查看这篇文章,挺有意思的。http://www.oschina.net/news/5...

什么是Docker?

  在Docker之前,我们肯定要先了解Docker是什么。官网的介绍是“Docker is the world’s leading software container platform.”官方给Docker的定位是一个应用容器平台。至于为什么要做这个Docker,官网上还有这么一句话"Docker is an open platform for developers and sysadmins to build, ship, and run distributed applications, whether on laptops, data center VMs, or the cloud."这句话用一句非常简单的话去概括就是"Build once,Run anyWhere".这一点跟Java很像。那么它这样做是要解决现实中什么问题,我列举几个情况。
   1.合作开发的时候,在本机可以跑,别人的电脑跑不起来
   这里我们拿java Web应用程序举例,我们一个java Web应用程序涉及很多东西,比如jdk、tomcat、spring等等。当这些其中某一项版本不一致的时候,可能就会导致应用程序跑不起来这种情况。Docker则将程序直接打包成镜像,直接运行在容器中即可。
   2.服务器自己的程序挂了,结果发现是别人程序出了问题把内存吃完了,自己程序因为内存不够就挂了
  这种也是一种比较常见的情况,如果你的程序重要性不是特别高的话,公司基本上不可能让你的程序独享一台服务器的,这时候你的服务器就会跟公司其他人的程序共享一台服务器,所以不可避免地就会受到其他程序的干扰,导致自己的程序出现问题。Docker就很好解决了环境隔离的问题,别人程序不会影响到自己的程序。
  3.公司要弄一个活动,可能会有大量的流量进来,公司需要再多部署几十台服务器
  在没有Docker的情况下,要在几天内部署几十台服务器,这对运维来说是一件非常折磨人的事,而且每台服务器的环境还不一定一样,就会出现各种问题,最后部署地头皮发麻。用Docker的话,我只需要将程序打包到镜像,你要多少台服务,我就给力跑多少容器,极大地提高了部署效率。

Docker与虚拟机的区别

关于Docker与虚拟机的区别,我在网上找到的一张图,非常直观形象地展示出来,话不多说,直接上图。

图片描述

图片描述

  比较上面两张图,我们发现虚拟机是携带操作系统,本身很小的应用程序却因为携带了操作系统而变得非常大,很笨重。Docker是不携带操作系统的,所以Docker的应用就非常的轻巧。另外在调用宿主机的CPU、磁盘等等这些资源的时候,拿内存举例,虚拟机是利用Hypervisor去虚拟化内存,整个调用过程是虚拟内存->虚拟物理内存->真正物理内存,但是Docker是利用Docker Engine去调用宿主的的资源,这时候过程是虚拟内存->真正物理内存。

Docker安装

  Docker的安装非常简单,官网上都给出了具体的安装步骤,都是可视化的安装,三步就搞定了。这里我就不细说了,附上Docker的官网安装教程地址。
Mac:https://docs.docker.com/docke...
Windows:https://docs.docker.com/docke...
linux的话,官网有不同版本的安装的教程,这里放上ubuntu的安装教程,其他小伙们自行查看。
ubuntu:https://docs.docker.com/engin...

Docker三个基本概念

  下面这张图非常的经典,很形象地展示了,什么是容器,什么是镜像,什么是仓库,以及三者之间的联系。
图片描述

  接下来我们来解释一下这张图。现在我们要造一间厨房,在造之前我们首先要干的一件事,就是先列举出我们造厨房需要的东西。我们可能需要一个通了水电煤的房子以及一些必需的厨房用具诸如锅碗瓢勺、煤气灶、冰箱、水槽等等这些东西。现在我们知道需要了什么东西之后,我们就去找这些东西。首先我们先去京东购买一些厨房用具,这些用具就好比我们的Docker镜像,我们厨房的用具到了之后得找个地方把它们放在,不可能随处丢吧,不然后面用的时候就找不到它了,那么我们Docker镜像也是这样,需要一个Docker仓库去存储这些镜像。现在我们有了这些厨房用具之后就可以做饭了吗?答案当然是不能,没水没电没火啊!这时候我们得把厨房用具给装到一个通了水电煤的房子才行,那么Docker镜像也是这样,单纯的Docker镜像是不能用的,它得装到Docker容器中通了水电煤才能使用。等我们装好了厨房用具之后我们就可以开始做饭,那么我们的Docker镜像装到Docker容器之后,我们应用就可以跑起来了。

我的第一个Docker镜像

上面的文章中,我们了解了一下Docker,接下来我们学着做一个属于自己的Docker镜像。

第一步:从镜像中心下载一个Node镜像

    a).去http://hub.daocloud.io/上找到Node镜像地址并执行如下命令
      docker pull daocloud.io/library/node:标签
    b).查看本地库的Docker镜像,是否下载完成
      docker images

结果如下:
图片描述

第二步:写一个app.js

app.js的内容如下,内容很简单,作用也很简单,起一个80端口的服务,页面显示“Hello Docker”。


var http = require(''http'');

http.createServer(function (request, response) {

    // 发送 HTTP 头部
    // HTTP 状态值: 200 : OK
    // 内容类型: text/plain
    response.writeHead(200, {''Content-Type'': ''text/plain''});

    // 发送响应数据 "Hello World"
    response.end(''Hello Docker\n'');
}).listen(3000);

// 终端打印如下信息
console.log(''Server running at http://127.0.0.1:3000/'');

第三步:写一个Dockerfile,用于构建镜像

#依赖的镜像
FROM daocloud.io/library/node:latest
#镜像创建者的信息
MAINTAINER wuming "wuming@maihaoche.com"
#执行mkdir helloDocker创建一个helloDocker文件夹
RUN mkdir helloDocker
#将app.js添加到helloDocker文件夹中
ADD app.js  helloDocker
#容器运行时启动的命令,下面命令等价于 CMD node /helloDocker/app.js
CMD ["node","/helloDocker/app.js"]

第四步:执行命令,构建对象

docker build -t hello-docker .
//-t 用于指定镜像的name:tag,没有指定tag则是latest。
//. 代表Dockerfile 所在的路径,这里是当前目录

结果如下:
图片描述

如果镜像仓库或者标签错了的话,可以使用如下命令进行修改

    docker tag IMAGE ID name:tag
   例: docker tag 1786dad83d25 hello-docker:1.0.0

结果如下:
图片描述
这里需要注意的是“docker tag”是新建一个标签用于目标镜像指向源镜像,所以我们记得删除原来的标签

docker rmi name:tag
例:docker rmi hello-docker:latest

图片描述

温馨提示:当前目录应该有Dockerfile和app.js才可用上面命令构建成功
图片描述

我的第一个Docker容器

docker run  -d -p 8003:3000 IMAGE ID

注意事项:
-p IP:host_port:container_port-p IP::port 来指定允许访问容器的主机上的 IP、接口等,我这里只允许外部8080端口来访问3000端口
-d 就是在后台运行容器,并返回容器ID。有兴趣的小伙伴可以体验一下有-d和没-d的区别

这时我们输入http://localhost:8003/就可以访问了我们刚刚启的服务了。
图片描述

另外我们可以通过docker ps 来查看当前所有启动的容器。
接下来我们进入容器中看看这个容器到底有什么东西!

docker exec -it <CONTAINER ID 或者 NAMES> /bin/bash

图片描述

进入容器中我们就看到了刚刚我们之前在Dockerfile建的helloDocker文件夹,进去之后发现了我们的app.js.执行exit命令就可以退出容器。

Docker 数据卷的使用

  到这里我们已经掌握了如何去制作一个镜像以及如何启动一个镜像了,接下来我们要说点难一点的东西Docker数据卷的使用。首先说一下什么是卷,为什么要引入卷这个东西。
  这边我们拿前端开发来说,我们在前端开发中,我们的js、css以及一些图标等等这些静态资源,这些文件都是可以直接在url输入地址直接访问到的,本身这些东西不会影响项目的运行,说点通俗点就是我们的这些资源怎么整不会让服务器报500错误.按照我们前面的方法去做的话,肯定之间把这些文件全部打到镜像里面,但是这个方法有个很不好的地方的。就是我们每次修改他们都需要重新打一次镜像,就感觉很繁琐。于是我们就想,我们能不能把这次静态资源从镜像中抽离出来,我们让容器指向这个目录,然后我们的服务就可以访问这些资源,每次改变完之后我们就不需要重新打镜像了,这样岂不是很好。正因为如此我们的数据卷就出来了,用来解决这个问题,那么什么是数据卷呢?我们用一句话来阐述就是“数据卷是一个可供一个或多个容器使用的特殊目录。”接下来我们实际操作一把。

数据卷

第一步:我们建立一个myDocker文件,里面包含两个文件app.js和package.json

app.js的内容如下:

var express = require(''express'');
var app = express();
//设置public目录为静态目录
app.use(express.static(''public''));
//监听3000端口
app.listen(3000);

package.json内容如下(主要目的是安装express,自己可以npm init自己生成一下,不必抄我的):

{
  "name": "docker",
  "version": "1.0.0",
  "description": "docker test",
  "main": "app.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.15.3"
  }
}

第二步:在myDocker同级目录下,新建一个public目录,放张图片进去,这里我放了一张美女图片(hhh)。

图片描述

第三步:在myDocker同级目录下新建一个Dockerfile,内容如下:

#设置基础镜像
FROM daocloud.io/library/node:latest
#维护者信息
MAINTAINER wuming wuming@maihaoche.com
#在容器中新建一个myDocker文件中
RUN mkdir myDocker
#将Dockerfile所在目录中myDocker文件夹的内容加到目标容器中的myDocker文件夹中
ADD myDocker  /myDocker
#设置工作目录
WORKDIR /myDocker
#执行安装项目依赖包的命令
RUN npm install
#容器启动时,执行node app.js
CMD node app.js

第四步:构建一个名为my-docker的镜像

当前目录结构为:
myDocker
  ----Dockerfile
  ----myDocker
    ----app.js
    ----package.json
  ----public
构建命令

docker build -t my-docker:latest .

第五步:以挂载方式启动容器

 docker run -d -p 8003:3000 -v /Users/wuming/dockerWorkpace/myDocker/public:/myDocker/public  my-docker:latest

这里我将本机的myDocker下的public挂载到容器中的/myDocker/public下,这里我们可以登入容器中看一下,会发现我们镜像并没有将我们的public目录打包进去,但是我们的容器中却有这个public目录。
图片描述

第六步:访问我们的图片

在游览器地址栏中输入http://localhost:8003/test.jpg便可看见,如下效果:图片描述
大家可以往自己的本机的public丢图片进去,再去访问它试试看,以及看看容器中的public没有动态改变,答案是可以的。

数据卷容器

上面我们已经实现了数据卷,但是我们发现加入如果我要起多个容器服务,时间短还能记得这个容器挂载目录,要是时间多了岂不是都忘了,而且每次这样去挂载也挺麻烦的,我们能不能把我们已经配好数据卷的容器作为一个数据卷的提供者呢?答案是可以的,这就是我们要说的数据卷容器,接下来我们操作一下。
docker run -d -p 8005:3000 --volumes-from NAMES或者CONTAINER ID  my-docker:latest
上面的NAMES或者CONTAINER ID为我们配好的数据卷的容器。启动完容器之后,我们可以访问试一下,并且往挂载目录丢点其他图片进去,看看两个容器服务是不是都可以访问到。答案是可以的。

使用网络

外部访问容器

1.映射所有接口地址
-p <端口号(8003)>:<端口号(3000)>

任何ip地址的指定<端口号(8003)>都可以访问到服务(前提是那ip地址对应的主机要起了这个服务)

2.映射到指定地址的指定端口
-p <ip地址(127.0.0.1)><端口号(8003)>:<端口号(3000)>

指定ip地址的<端口号(8003)>都可以访问到服务(前提是那ip地址对应的主机要起了这个服务),比如指定127.0.0.1,只有本机输入127.0.0.1:8003或者localhost:8003可以访问自己的服务,但是其他同一个局域网的手机输入本机的对应局域网的ip地址:8003是访问不到的,小伙们可以拿自己的手机试试。

Docker常用命令

1.杀死所有正在运行的容器

docker kill $(docker ps -a -q)

2.删除所有已经停止的容器

docker rm $(docker ps -a -q)

3.删除所有镜像

docker rmi $(docker images -q)

4.关闭容器

docker stop CONTAINER ID或者NAMES

5.重新启动关闭的容器

docker start CONTAINER ID或者NAMES

6.移除本地容器

docker rm CONTAINER ID或者NAMES

7.查看本地容器

docker ps  //查看正在运行的容器
docker ps -a //查看所有容器

8.查看本地镜像

 docker images

9.创建镜像

 docker build -t name:tag Dockerfile路径  

10.修改本地镜像标记

 docker tag  IMAGE ID  name:tag 
 docker rmi name:tag

11.删除本地镜像

docker rmi name:tag或者IMAGE ID

12.进入容器

docker exec -it IMAGE ID或者NAMES /bin/bash

13.获取镜像中心的镜像

docker pull name:tag

14.获取容器的端口映射配置

docker port CONTAINER ID或者NAMES

持续更新更新....

Dockerfile命令速查表

FROM 命令

FROM <image>:<tag>

用于设置基础镜像,一般是Dockerfile的第一句。
如果没有指定 tag ,则默认tag是latest。

MAINTAINER

MAINTAINER <name>

用来指定维护者的姓名和联系方式。

RUN

RUN <command> 或 RUN ["executable", "param1", "param2"]

每条 RUN 指令将在当前镜像基础上执行指定命令,并提交为新的镜像。

ADD

ADD <src> <dest>

将 <src> 文件复制到 <dst> 文件:<src> 是相对被构建的源目录的相对路径,可以是文件或目录的路径,也可以是一个远程的文件 url,<dst> 是容器中的绝对路径。

COPY

COPY <src> <dest>

复制本地主机的 <src> (为Dockerfile所在目录的相对路径)到容器中的 <dest>,与ADD指令差不多

ENTRYPOINT

ENTRYPOINT ["executable", "param1", "param2"] :推荐使用的 exec 形式

ENTRYPOINT command param1 param2 :shell 形式

配置容器启动后执行的命令,并且不可被 docker run 提供的参数覆盖。
一个 Dockerfile 中只能有一个 ENTRYPOINT,如果有多个,则最后一个生效。

CMD

CMD ["executable","param1","param2"] 使用 exec 执行,推荐方式;
CMD command param1 param2 在 /bin/sh 中执行,提供给需要交互的应用;
CMD ["param1","param2"] 提供给 ENTRYPOINT 的默认参数;

指定启动容器时执行的命令,每个 Dockerfile 只能有一条 CMD 命令。如果指定了多条命令,只有最后一条会被执行。

如果用户启动容器时候指定了运行的命令,则会覆盖掉 CMD 指定的命令。

WORKDIR

WORKDIR /path/to/workdir

为后续的 RUN、CMD、ENTRYPOINT 指令配置工作目录。

可以使用多个 WORKDIR 指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。例如

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

则最终路径为 /a/b/c 。

EXPOSE

EXPOSE <port> [<port>...]

告诉 Docker 服务端容器暴露的端口号,供互联系统使用。
例如 EXPOSE 8080 3000,开放 8080 和 3000 端口。

ENV

ENV <key> <value>

指定一个环境变量,会被后续 RUN 指令使用,并在容器运行时保持。

VOLUME

VOLUME ["/data"]

创建一个可以从本地主机或其他容器挂载的挂载点,一般用来存放数据库和需要保持的数据等。

USER

USER <UID/Username>

为容器内指定 CMD RUN ENTRYPOINT 命令运行时的用户名或UID。

ONBUILD

ONBUILD [INSTRUCTION]

配置当所创建的镜像作为其它新创建镜像的基础镜像时,所执行的操作指令。
例如,利用Dockerfile创建了一个镜像A,其中Dockerfile中有这么几个命令

ONBUILD RUN mkdir test
ONBUILD ADD app.js /test/app.js

那么镜像B基于镜像A去构建的时候,默认会在最后加上这两行命令

 FROM 镜像A
 RUN mkdir test
 ADD app.js /test/app.js

ES6入门,看这篇就够了

ES6入门,看这篇就够了

ES6从入门到放弃

1.ES6是什么,黑历史,不讲,自己百度去。

2.在浏览器中如何使用?

1.babel babeljs.io在线编译

2.traceur-----Google出的编译器,把ES6编译成ES5

traceur 编译

bootsrap 引导程序,和我们理解的bootstrap css那个框架没有半毛钱关系。

<script src="traceur.js"></script>
<script src="bootstrap.js"></script>
<script type="module">
</script>

3.es6新功能

1.定义变量

let 替代var 拥有块级作用域。

2.const

定义常量 常量不可以修改,如果修改会报错

3.字符串连接

ES6管它叫模板引擎,其实就是字符串连接这个破玩意。

用键盘1左边那个键,反引号。

var str =    ''帅'';//注意这里是正常双引号
var str2 = `你们从我脸上看到了什么${str},难道不是么?`;

4.解构赋值

var [a,b,c] = [12,5,8];

console.log(a,b,c);//12,5,8 是不是很简洁?

5.ES6新技巧

a)复制数组

arr2.from(arr)//再也不用用循环了,吼吼,还可不可以更性感一点

arr2 = [...arr]//简直是bug级别的用法,不过真心简单

b)for of循环

c)map 、delete

6.箭头函数 重点,angular 2.x用的不少。

/*function show(a){
return a;
}
var s=show(12);
alert(s);*/

var show=(a,b)=>a+b;

var s=show(12,5);

相当简单。

([param] [, param]) => {
   statements
}
param => expression

解释:

param 是参数,根据参数个数不同,分这几种情况:
() => { … } // 零个参数用 () 表示
x => { … } // 一个参数可以省略 ()
(x, y) => { … } // 多参数不能省略 ()

7.更简洁的对象语法

var name=''abb'';
var age=101;
var preson={
    name,
    age,
    showName(){
        return this.name;
    },
    showAge(){
        return this.age;
    }
};
alert(preson.showName());

你没有看错,bug一样的语法。

8.重中之重,不懂这个你彻底懵逼,完全看不懂angular2.x在干啥。类。

类,真特么累,一个语法糖,废柴,然而你还得懂。

class Person{ //类
    constructor(name,age){
        this.name=name;
        this.age=age;
    }
    showName(){
        return this.name;
    }
    showAge(){
        return this.age;
    }
}

var p1=new Person(''aaa'',10);

alert(p1.showName());

你没有看错,这里面的this就没有问题的。玩类,不得不说的一个东西就是继承,这里得感谢ES6,我们再也不用

apply 和 constructor 混合的方式继承了,太特么恶心了,直接用extend,鼓掌,送火箭。

class Person{ //类
    constructor(name=''default'',age=0){
        this.name=name;
        this.age=age;
      }
    showName(){
        return this.name;
    }
    showAge(){
        return this.age;
    }
}

//继承
class Worker extends Person{
    constructor(name,age,job=''扫地的''){
        super(name,age);
        this.job=job;
    }
    showJob(){
        return this.job;
    }
}

早特么应该这么玩,话说吐槽一下,敢不敢把construtor这么丑逼的东西给我换成真正的构造函数?

再说一个模块化,前方高能,必须必须必须会!!否则你载angular2.x中连代码你都组织不起来,不知道你往哪里写。

之前我们用过seajs,和require,有了ES6,再也不用这两个货了。

定义模块

const a = 12;
export a;

再来一个angular2.x中的例子

class hero{
    //属性
      xxxx
      //方法
}

exports hero;

怎么使用?

import modA from ''./a.js'';
import modB from ''./b.js'';

console.log(modA+modB);

9.promise

如果你想玩一些新的库,肯定离不开数据交互,外面炒的很火。

就是一个对象,用来传递异步操作的数据(消息)

pending(等待、处理中)—> Resolve(完成、fullFilled)
              —> Rejected(拒绝、失败)

使用:

var p1=new Promise(function(resolve,reject){
    //resolve  成功了
    //reject    失败了
        });

var p1=new Promise(function(resolve,reject){
    if(异步处理成功了){
        resolve(成功数据)
    }else{
        reject(失败原因)
    }
        });

p1.then(成功(resolve),失败(reject))    √
--------------------------------------------
p1.catch——用来捕获错误

10.生成器Gennerator.

看起来挺悬,其实就是那么回事儿,我们看看是什么鬼。

一个函数,可以遍历状态,感觉就是状态机,好吧不说了再说就懵逼了。

语法:

function *show(){

}

注意:

a)函数名字前有星号

b)内部用yield

c)next执行下一个状态

function* show(){
     yield ''Hello'';
     yield ''World'';
     yield ''Es6'';
     return ''well'';
}

var res=show();
console.log(res.next()); //{value:''Hello'', done:false}
console.log(res.next()); //{value:''World'', done:false}
console.log(res.next()); //{value:''Es5'', done:false}
console.log(res.next()); //{value:''well'', done:true}

ES6基本上会这么多就可以了,剩下的时间就可以去泡妹子了,JS大法好,收工!

JS正则表达式入门,看这篇就够了

JS正则表达式入门,看这篇就够了

前言

在正文开始前,先说说正则表达式是什么,为什么要用正则表达式?正则表达式在我个人看来就是一个浏览器可以识别的规则,有了这个规则,浏览器就可以帮我们判断某些字符是否符合我们的要求。但是,我们为什么要使用正则表达式呢?下面我们就看一下下面这个业务场景。

验证QQ号的合法性
/**
*合法qq号规则:1、5-15位;2、全是数字;3、不以0开头
*/

//1.在不使用正则表达式的时候,我们可能会这样判断QQ号的合法性
var qq="6666666a6666";         
if(qq.length>=5&&qq.length<=15&&!isNaN(qq)&&qq.charCodeAt(0)!=48){
        alert("QQ合法");
    }else{
        alert("QQ不合法")
    }
    
//2.使用正则表达式
    var qq="066336";
    var reg=/^[1-9][0-9]{4,14}$/;
    if(reg.test(qq)){
        alert("QQ合法");
    }else{
        alert("QQ不合法");
    }

从上面这个例子可以看出来使用了正则表达式的时候,我们的代码量变少了,而且比较直观。如果遇到非常的复杂的匹配,正则表达式的优势就更加明显了。

使用方法

接着上面,我想先说说JS正则表达式是如何使用的。非常简单,只有两步而已。

第一步:定义一个正则表达式

定义正则表达式有两种方法,第一种通过"/正则表达式/修饰符"这种形式直接写出来,第二种通过“new RegExp(''正则表达式'',''修饰符)''”创建一个RegExp对象。其中修饰符为可选项,有三个取值g:全局匹配;i:不区分大小写;m:多行匹配

//第一种“/正则表达式/”
    var reg1=/hello \w{3,12}/g;
//第二种new RegExp(''正则表达式'')
    var reg2=new RegExp("hello \\w{3,12}",''g'');
    
/**
*这里需要注意的是,第二种方法中由于字符串转义问题,"\\"代表"\"。
*/   

上面这个定义方法,其实还有一个可选参数(修饰符),这里我们先不深入探究,后面我们再细说。

说到RegExp对象,下面要说一下RegExp对象自带的属性,并不复杂,这里我就列一下,不展开说了。

属性 描述
global RegExp 对象是否具有标志 g。
ignoreCase RegExp 对象是否具有标志 i。
lastIndex 一个整数,标示开始下一次匹配的字符位置。
multiline RegExp 对象是否具有标志 m。
source 正则表达式的源文本。

第二步:调用RegExp对象中的方法

RegExp对象给我们提供了三种方法供我们使用,分别是test()、exec()和compile()。下面具体说一下这三个方法的用处。

1.test()

检索字符串中指定的值。返回 true 或 false。这个是我们平时最常用的方法。

 var reg=/hello \w{3,12}/;
 alert(reg.test(''hello js''));//false
 alert(reg.test(''hello javascript''));//true

2.exec()

检索字符串中指定的值。匹配成功返回一个数组,匹配失败返回null。

var reg=/hello/;
console.log(reg.exec(''hellojs''));//[''hello'']
console.log(reg.exec(''javascript''));//null

3.compile()

compile() 方法用于改变 RegExp。
compile() 既可以改变检索模式,也可以添加或删除第二个参数。


var reg=/hello/;
console.log(reg.exec(''hellojs''));//[''hello'']
reg.compile(''Hello'');
console.log(reg.exec(''hellojs''));//null
reg.compile(''Hello'',''i'');
console.log(reg.exec(''hellojs''));//[''hello'']

如何写一个正则表达式

第一次接触正则表达式同学们,可能被这个正则表达式的规则弄得迷迷糊糊的,根本无从下手。小编我第一次学这个正则表达式的时候,也是稀里糊涂,什么元字符、量词完全不知道什么东西,云里雾里的。后面小编细细研究了一下,总结一套方法,希望可以帮助大家。

关于正则表达式书写规则,可查看w3school,上面说的很清楚了,我就不贴出来了。我就阐述一下我写正则表达式的思路。

其实正则表达式都可以拆成一个或多个(取值范围+量词)这样的组合。针对每个组合我们根据JS正则表达式的规则翻译一遍,然后将每个组合重新拼接一下就好了。下面我们举个例子来试一下,看看这个方法行不行。

验证QQ号的合法性

合法qq号规则:1、5-15位;2、全是数字;3、不以0开头

第一步:拆成(取值范围+量词)这样的组合

根据QQ号的验证规则,我们可以拆成两个(取值范围+量词)的组合。分别是:

1.(1~9的数字,1个);2.(0~9的数字,4~14个)
第二步:根据正则表达式规则翻译(取值范围+量词)
1.(1~9的数字,1个)     =>   [1-9]{1}或者[1-9]
2.(0~9的数字,4~14个)  =>   [0-9]{4,14}
第三步:将翻译好的(取值范围+量词)组合进行拼接

初学者可能在拼接这一步会犯一个错误,可能会组合拼接成这个样子/[1-9]{1}[0-9]{4,14}/或者简写翻译成/[1-9] [0-9]{4,14}/这些都不对的。调用test()方法的时候,你会发现只要一段字符串中有符合正则表达式的字符串片段都会返回true,童鞋们可以试一下。

var reg=/[1-9][0-9]{4,14}/;
alert(reg.test(''0589563''));
//true,虽然有0,但是''589563''片段符合
alert(reg.test(''168876726736788999''));
//true,这个字符串长度超出15位,达到18位,但是有符合的字符串片段

正确的写法应该是这样的:

/^[1-9][0-9]{4,14}$/(用^和$指定起止位置)

下面我们看一个复杂点的例子:

验证国内电话号码

0555-6581752、021-86128488

第一步:拆成(取值范围+量词)这样的组合

这里会拆成两个大组合:

1、(数字0,1个)+(数字0~9,3个)+("-",1个)+(数字1~9,1个)+(数0~9,6个)
2、(数字0,1个)+(数字0~9,2个)+("-",1个)+(数字1~9,1个)+(数0~9,7个)
第二步:根据正则表达式规则翻译(取值范围+量词)
1、([0-0],{1})+([0-9],{3})+"-"+([1,9],{1})+([0,9],{6})
2、([0-0],{1})+([0-9],{2})+"-"+([1,9],{1})+([0,9],{7})
第三步:将翻译好的(取值范围+量词)组合进行拼接

这里我们先拼接一个大组合,然后再将大组合拼接起来

1、0[0-9]{3}-[1-9][0-9]{6}
2、0[0-9]{2}-[1-9][0-9]{7}

最后拼接为:

/(^0[0-9]{3}-[1-9][0-9]{6}$)|(^0[0-9]{2}-[1-9][0-9]{7}$)/

正则表达式拓展

除了RegExp对象提供方法之外,String对象也提供了四个方法来使用正则表达式。

1.match()

在字符串内检索指定的值,匹配成功返回存放匹配结果的数组,否则返回null。这里需要注意的一点事,如果没有设置全局匹配g,返回的数组只存第一个成功匹配的值。


var reg1=/javascript/i;
var reg2=/javascript/ig;
console.log(''hello Javascript Javascript Javascript''.match(reg1));
//[''Javascript'']
console.log(''hello Javascript Javascript Javascript''.match(reg2));
//[''Javascript'',''Javascript'',''Javascript'']

2.search()

在字符串内检索指定的值,匹配成功返回第一个匹配成功的字符串片段开始的位置,否则返回-1。

var reg=/javascript/i;
console.log(''hello Javascript Javascript Javascript''.search(reg));//6

3.replace()

替换与正则表达式匹配的子串,并返回替换后的字符串。在不设置全局匹配g的时候,只替换第一个匹配成功的字符串片段。

var reg1=/javascript/i;
var reg2=/javascript/ig;
console.log(''hello Javascript Javascript Javascript''.replace(reg1,''js''));
//hello js Javascript Javascript
console.log(''hello Javascript Javascript Javascript''.replace(reg2,''js''));
//hello js js js

4.split()

把一个字符串分割成字符串数组。

var reg=/1[2,3]8/;
console.log(''hello128Javascript138Javascript178Javascript''.split(reg));
//[''hello'',''Javascript'',''Javascript178Javascript'']

结语

正则表达式并不难,懂了其中的套路之后,一切都变得简单了。在最后我想说点题外话,网上不乏一些文章记录一些常用的正则表达式,然后新手前端在使用正则表达式的时候都会直接拿来就用。在这里我想说一下自己的看法,这些所谓记录常用的正则表达式文章并非完全都是正确的,有不少都是错的。所以同学们在日后使用的过程尽量自己写正则表达式,实在不会了可以去参考一下,但真的不要照搬下来。咱不说这种会影响自己成长的话,咱就说你抄的一定都是对的吗?多思考一下,总没有坏处。

我们今天的关于入门Webpack,看这篇就够了webpack入门教程的分享就到这里,谢谢您的阅读,如果想了解更多关于C++内存问题,看这篇就够了、Docker入门,看这篇就够了、ES6入门,看这篇就够了、JS正则表达式入门,看这篇就够了的相关信息,可以在本站进行搜索。

在本文中,我们将带你了解react入门之使用webpack搭配环境在这篇文章中,我们将为您详细介绍react入门之使用webpack搭配环境的方方面面,并解答常见的疑惑,同时我们还将给您一些技巧,以帮助您实现更有效的(24/24) webpack小案例--自己动手用webpack构建一个React的开发环境、1 使用webpack搭建vue开发环境、React 基础实践教程(一):搭建简单的 React + ES6/7 + Webpack 开发环境、React+webpack搭建前端开发环境(一)

本文目录一览:

react入门之使用webpack搭配环境(一)(react配置webpack)

react入门之使用webpack搭配环境(一)(react配置webpack)

react入门之搭配环境(一)

如果你想直接上手开发,而跳过这些搭配环境的繁琐过程,推荐你使用官方的create-react-app命令

npm install -g create-react-app  //安装create-react-app脚手架   npm为node.js的包管理工具,请确保你已经安装了node.js
create-react-app my-app    //使用create-react-app创建,my-app为项目名称
cd my-app/     //进入my-app目录
npm start        //运行项目

现在打开http://localhost:3000/就能看到初始界面

我不大愿意使用官方自带这个脚手架,是因为它的webpack配置太复杂,我比较愚钝看不大懂,还望有大神能研究透彻分享一下。

要如何create-react-app内部的webpack配置文件解压出来?

npm run eject

源代码

每次看教程我都喜欢先把项目跑起来,然后再一句一句代码地去了解。如果你也是:

git clone https://github.com/lingjiawen/HelloReact.git
cd HelloReact/
npm install
npm run dev

一、创建项目结构

新建一个文件夹,命名为HelloReact

顺口提一下,我用的IDE是Sublime

在该文件夹内这样组织你的项目结构:

|--app                       //项目组件
   |--components                  //组件结构
      |---Hello.jsx
   |--main.js                     //入口文件
|--build                     //项目build文件
   |--index.html                 //索引html
|--.babelrc //babel转码工具配置文件
|--package.json //npm说明文件,可以理解为包管理文件 |--webpack.config.js //webpack配置文件

在build/index.html中拷贝以下代码:

<!DOCTYPE html>
<html>
    <head>
        <Meta charset="UTF-8">
        <title>ReactDemo1</title>
    </head>
    <body>
        <!--插入React组件的位置-->
        <div >
        </div>
        <script src="bundle.js"></script>
    </body>
</html>            

React代码插入到main中,而bundle.js是webpack打包生成的js文件,在这里你可以先帮它记下来下文看到了再回来一看就清楚了。

在package.json中输入以下代码:

注意,所有的注释都不要输入进去!

//package.json
{
  "name": "HelloReact",//项目名字
  "version": "1.0.0",//项目版本
  "main": "webpack.config.js","scripts": {
    "start": "webpack",//npm start的配置
  },"author": "",//作者
  "license": "ISC","devDependencies": {
    //调试依赖项
    "babel-core": "^6.25.0","babel-loader": "^7.1.1","babel-plugin-react-transform": "^2.0.2","babel-preset-es2015": "^6.24.1","babel-preset-react": "^6.24.1","react": "^15.6.1","react-dom": "^15.6.1","react-transform-hmr": "^1.0.4","webpack": "^3.4.1",  },"description": "","dependencies": {
    //项目依赖项

  }
}

在webpack配置文件webpack.config.js中输入以下代码:

var webpack = require('webpack');//引入Webpack模块,注意这里只能使用ES5语法引入


module.exports = {
    entry: __dirname + "/app/main.js",//唯一入口文件
    output: {
        path: __dirname + "/build",//打包后的bundle.js文件存放的地方
        filename: "bundle.js"      //打包后的文件名
    },module: {
        //loaders加载器
        loaders: [
            {
                test: /\.(js|jsx)$/,//匹配loaders所处理的文件的扩展名的正则,如jsx和js文件
                loader: 'babel-loader' //loader的名称
            }
        ]
    },plugins: [
        new webpack.HotModuleReplacementPlugin()//热模块替换插件
    ]
};

.babelrc是babel转码器的配置文件,它能将es6代码转换成es5代码,还支持react语法转换

在.babelrc中输入以下代码:

//.babelrc
{
  "presets": [
    "react","es2015"
  ],"env": {
    "development": {
      "plugins": [
        [
          "react-transform",{
            "transforms": [
              {
                "transform": "react-transform-hmr","imports": [
                  "react"
                ],"locals": [
                  "module"
                ]
              }
            ]
          }
        ]
      ]
    }
  }
}

在app/components/Hello.jsx中输入以下代码:

import React from 'react';       //引入react

//创建组件类:名字首字母必须大写
class Hello extends React.Component {
    render() {
        return (
            <div>Hello World!</div>
        )
    }
}

//导出组件
export default Hello;

React 使用 JSX 来替代常规的 JavaScript。

JSX 是一个看起来很像 XML 的 JavaScript 语法扩展。

它看起来是在Javascript代码里直接写XML的语法,而实质上是一个语法糖,每一个XML标签都会被JSX转换工具(如babel)转换成纯Javascript代码

在app/main.js中输入以下代码:

//main.js
import React from 'react';
import ReactDOM from 'react-dom';
import Hello from './components/Hello.jsx';

ReactDOM.render(
    <Hello />,document.getElementById('main')
);

二、运行项目

打开命令行,cd到HelloReact文件夹目录,运行

npm install

该命令会安装package.json的所有依赖文件,安装完成后,运行:

npm start

npm start命令是刚才你在package.json中定义的:

"scripts": {
    "start": "webpack",}

注:只有start可以省略run,其他都需要加上run,如 npm run dev;

你会发现build.js中多了bundle.js文件,这是你在webpack.config.js中定义的打包文件:

var webpack = require('webpack');//引入Webpack模块,注意这里只能使用ES5语法引入

module.exports = {
    ……
    output: {
        path: __dirname + "/build",……
};

打开该目录下的index.html,看到以下输出运行成功:

三、添加热替换模块

项目已经可以运行了,但是每次输完代码都要npm start进行打包,想想都没办法忍受,这就需要用到webpack-dev-server热替换模块,所见即所得

其实在前面的代码中,为了避免麻烦,我已经偷偷将热替换模块的部分配置加了进去

webpack.config.js中的

……
plugins: [
        new webpack.HotModuleReplacementPlugin()//热模块替换插件
    ]
……

package.json中的

 "devDependencies": {
    ……
    "babel-plugin-react-transform": "^2.0.2","webpack-dev-server": "^2.6.1"
    ……
  }

还有.babelrc中的

"env": {
    "development": {
      "plugins": [
        [
          "react-transform","locals": [
                  "module"
                ]
              }
            ]
          }
        ]
      ]
    }
  }

你可以将这些代码删除,发现也是可以正常打包并运行。因为之前并没有用到热加载

那我现在想用,要怎么用呢?

很简单,在package.json中加入:

……  
"scripts": {
    "start": "webpack","dev": "webpack-dev-server"
  }
……

好,现在运行一下npm run dev

打开localhost:8080

咦,怎么是列出文件列表,哦哦哦,原来是没有配置默认路径;

……  
"scripts": {
    "start": "webpack","dev": "webpack-dev-server --contentBase='./build' "
  }
……

重新运行npm run dev:

修改app/components/Hello.jsx

保存后再回到页面,发现页面已经自动更新:

(24/24) webpack小案例--自己动手用webpack构建一个React的开发环境

(24/24) webpack小案例--自己动手用webpack构建一个React的开发环境

通过前面的学习,对webpack有了更深的认识,故此节我们就利用前面相关知识自己动手用webpack构建一个React的开发环境,就算是一个小案例吧。

注:此处使用的开发工具是Webstorm。

1.安装webpack

1.1 新建文件夹

在安装webpack之前,我们先建立一个文件夹,并利用开发工具webstorm打开刚才新建的文件夹(或者新建项目,其项目目录选择刚才新建的文件夹),然后使用webstorm终端初始化webpack项目,命令如下:

npm  init -y

-y:表示默认初始化所有选项。

 初始化成功后可以在项目根目录下看到package.json文件。

1.2 安装webpack

package.json文件建立好以后,开始安装webpack,同样是在webstorm终端输入安装命令(可以使用npm安装也可以使用cnpm安装):

npm install --save-dev webpack@3.8.1

--save-dev:安装到该项目目录中。 

注意:此处为了兼容,webpack使用3.8.1版本。

安装好后,则会在package.json里看到当前安装的webapck版本号。

2.配置webpack.config.js

在项目根目录建立webpack.config.js文件,这个文件是进行webpack配置的,先建立基本的入口和出口文件。

2.1 配置

var path =require(''path'');
module.exports = {
    //入口文件
    entry:''./app/index.js'',
    //出口文件
    output:{
        filename:''index.js'',
        path:path.resolve(__dirname,''dist'')
    }
}

2.2 根据结构修改项目目录

文件配置好后,我们要根据文件的结构改造我们的项目目录,我们在根目录下新建app和dist文件夹,然后进入app文件夹,新建一个index.js文件。

3. 新建index.html文件

在根目录新建index.html文件,并引入webpack设置中的出口文件,代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>webpack react案例</title>
</head>
<body>

</body>
<!--引入出口文件-->
<script src="./temp/index.js"></script>
</html>

4. 增加打包命令并测试配置是否成功

4.1 增加命令

打开package.json文件,在scripts属性中加入build命令。

"scripts": {
    "build": "webpack"
  },

4.2 测试配置

4.2.1 测试代码

操作为在入口文件中写入部分代码,并进行打包测试:

app/index.js文件:

function component(){
    var element = document.createElement(''div'');
    element.innerHTML = (''Hello webpack react'');
    return element;
}
document.body.appendChild(component());

4.2.2 打包操作

在终端中输入npm run build进行打包,如果没有出现错误,手动在浏览器中打开index.html,出现以下效果说明配置成功。

5. 开发服务器配置

到该步我们还缺少一个实时更新的服务,我们开始配置:

5.1 插件安装

此处为了兼容使用指定安装方式。安装的版本为2.9.7。

npm install --save-dev webpack-dev-server@2.9.7

5.2 配置webpack.config.js文件

devServer:{
    contentBase:''./'',
    host:''localhost'',
    compress:true,//启动压缩
    port:1818
}

5.3 新增命令

配置好后再packeage.json里增加一个scripts命令,我们起名叫server。

"scripts": {
    "build": "webpack",
    "server": "webpack-dev-server --open"
  },

–open表示是直接打开浏览器。

5.4 启动服务

在终端执行npm run server 命令启动服务。

npm run server

 浏览器自动打开,效果与前面手动结果一致。

6.配置自动刷新浏览器

到此我们修改代码时,浏览器不能自动刷新,无法实时呈现我们编写的代码结果,只能重复新打包才能生效。

解决方法为:使公共路径指向内存。temp是系统的临时文件,存放内存刷新值。

6.1 配置出口文件

在出口文件配置中加一个publicPath:’temp/’ 。

//出口文件
    output:{
        filename:''index.js'',
        path:path.resolve(__dirname,''dist''),
        publicPath:''temp/''
    }

6.2 修改index.html中引入的js文件

<script src="./temp/index.js"></script>

6.3 重新启动服务配置成功

关闭之前的服务,然后使用npm run server 重新启动服务。

npm run server

6.4 测试自动刷新浏览器是否成功

改写入口文件中内容,然后保存,可以看到浏览器自动刷新,渲染了最新的值,说明自动刷新配置成功。

7. Babel安装配置

在webpack中配置Babel需要先加入babel-loader,我们使用npm来进行安装,我们还需要支持es2015和React,所以要安装如下四个包:

此处为了兼容问题我使用指定版本的安装方式,如下:

npm  install --save-dev babel-core@6.26.3 babel-loader@7.1.4 babel-preset-es2015@6.24.1 babel-preset-react@6.24.1

当然你也可以使用下列方式安装最新的,出现版本问题在对应去调整即可(不过有些费时费力而已,呵呵)

npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react

 这里四个包的安装,这四个包是不能省略。安装后你会在package.json里看到这些包的版本如下:

"babel-core": "^6.26.3",
    "babel-loader": "^7.1.4",
    "babel-preset-es2015": "^6.24.1",
    "babel-preset-react": "^6.24.1",
    "webpack": "^3.8.1",
    "webpack-dev-server": "^2.9.7"

8. .babelrc配置

安装完成后,我们需要对我们的babel进行一些相关配置,使其对es6、react等进行支持。

8.1 新建  .babelrc

在项目根目录新建.babelrc文件,并把配置写到文件里。

.babelrc:

{
    "presets":["react","es2015"]
}

9.  配置module

.babelrc配置完成后,到webpack.config.js里配置module,也就是配置我们常说的loader。

module:{
    loaders:[
        {
            test:/\.(jsx|js)$/,//匹配掉js或者jsx的正则
            exclude:/node_modules/,//排除不操作的文件
            loaders:"babel-loader",//使用loader进行操作
        }
    ]
}

 10. 编写React

webpack通过上边的步骤,基本已经配置完成了,这里我们写一个React文件来进行测试一下。

10.1 安装React相关包

安装React和React-dom:

npm install --save react  react-dom

10.2 改写入口文件

安装完成后,我们改写app/index.js文件,把原来原生的JavaScript代码改成React代码。

app/index.js:

import React from ''react'';
import ReactDOM from ''react-dom'';
ReactDOM.render(
    <div>Hello react</div>,
    document.getElementById("app")
);

10.3 新增挂载点

在index.html中新增一个div层,设置id="app"作为react的挂载点。

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>webpack react案例</title>
</head>
<body>

<!--挂载点--> <div id="app"></div>

</body> <!--引入出口文件--> <script src="./temp/index.js"></script> </html>

11. 测试相关配置是否成功

当上述都配置完成后,使用npm run server 重新启动服务,若是出现失败,建议先把node_modules删除了,然后在使用 npm install 进行安装。

npm run server

如果在浏览器中看到了Hello react 这段文字,说明我们的配置是成功的。如下:

上述只是简单的配置了一些内容,还很不成熟,支持的较少,实际在现实开发中已经有很多做好的脚手架供我们使用,我们不必去造轮子。

若是在上述配置中出现什么问题,欢迎留言我们共同探讨。。。

 

1 使用webpack搭建vue开发环境

1 使用webpack搭建vue开发环境

1 先去node.js官网下载nodejs并且安装

安装成功之后在命令行输入node -v 回车,npm -v回车如果显示对应的版本号,说明node安装成功,自带的npm也安装成功

2 在d盘下创建一个目录比如demo目录

3 在命令行输入d:回车,然后在输入cd demo回车切换到创建的目录下:

4 然后用npm初始化该目录:npm init 回车,完成后会在demo目录下生成一个package.json的文件

5 在本地局部安装webpack

npm install webpack --save-dev

安装成功之后会在package.json文件中多一项配置

"devDependencies": {
    "webpack": "^4.26.0"
  }

6 接着在本地安装webpack-dev-server 他可以在开发环境中提供很多服务,比如启动一个服务器,热跟新,接口代理等

如果在devDependencies中包含webpack和webpack-dev-server,则表示安装成功

"devDependencies": {
"webpack": "^4.26.0",
"webpack-dev-server": "^3.1.10"
}

7 其实webpack就是一个.js的配置文件

 然后在demo目录下创建一个webpack.config.js文件,并初始化他的内容为:

 

var config = {


};

module.exports = config

8 然后再package.json的scripts里增加一个快速启动webpack-dev-server服务的脚本

9 当运行npm run dev命令时就是执行webpack-dev-server --open --config webpack.config.js命令

  --config是指向webpack-dev-server读取的配置文件

--open会在执行命令时自动在浏览器打开页面,默认地址是127.0.0.1:8080,ip和端口都是可以配置的:

10 配置webpack最重要的必选的入口entry和出口output

entry:告诉webpack从哪里寻找依赖,并且编译

output:用来配置编译后的文件存储位置和文件名

在demo目录下新建main.js作为入口的文件,然后再webpack.config.js中进行入口和出口的配置

//
var path=require("path");


var config = {
    entry:{
        //配置的单入口,webpack会从main.js文件开始工作
        main:''./main''
    },
    
    output:{
        
        //打包后文件的输出目录
        path:path.join(_dirname,''./dist''),
        //指定资源文件引用的目录
        publicPath:''/dist/'',
        //用于指定输出文件的名称
        filename:''main.js''
        
    }

};

module.exports = config

这里配置的output意思为打包后的文件会存储为demo/dist/main.js

11 在demo目录下新建一个index,html作为我们spa的入口

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>webpack app</title>
</head>
<body>
    <div id="app">
        hello word
    </div>

    <script type="text/javascript" src="/dist/main.js"></script>
</body>
</html>

12 在终端执行下面的命令就会在浏览器中打开index页面

npm run dev

如果运行后报错如下:

那是因为webpack 4以上把命令迁移到webpack-cli上,可以下局部运行如下命令来安装

npm i -D webpack-cli来安装

安装完之后继续运行npm run dev如果有如有如下错误

检查全局变量_dirname的下划线是不是两个__dirname,否则将显示如下页面

 

 

至此已经完成了webpack+vue配置的最重要一步了

13 webpack --progress --hide-modules

接下来逐步完善配置文件:

对于不同的模块需要用不同的加载器来处理,而加载器就是webpack最重要的功能,通过安装不同的加载器可以对各种文件后缀名进行处理

比如处理css样式:就要安装style-loader和css-loader,通过npm来安装

npm install css-loader --save-dev

npm install style-loader --save-dev

14 安装完加载器之后再webpack.config.js文件中配置加载器

    module:{

        //再module对象的rules属性可以指定一系列的loeders,每一个loader都必须包含test和use两个选项
        //此配置的意思:
        //当webpack编译过程中遇到require和import语句导入一个后缀名为.css的文件时
        //,先将他通过css-loader转换,再通过style-loader转换,然后继续打包
        rules:[

            {
                test:/\.css$/,
                use:[
                    ''style-loader'',
                    ''css-loader''
                ]
            }

        ]
    }

15 使用webpack的插件plugins

extract-text-webpack-plugin:提取css,生成一个main.css的文件

通过npm安装该插件

npm install extract-text-webpack-plugin --save-dev

16 安装完成后再webpack-config.js中导入插件,并改写loader的配置

var extractTextPlugin=require(''extract-text-webpack-plugin'');
            {
                test:/\.css$/,
                use:extractTextPlugin.extract({

                    use:''css-loader'',
                    fallback:''style-loader''
                })
            }
    plugins:[

        //重命名提取后的css文件
        new extractTextPlugin("main.css")
    ]

17 然后再index页面通过link的方式应用,如果报这种错误

 

(node:30260) DeprecationWarning: Tapable.plugin is deprecated. Use new API on `.hooks` instead
i 「wds」: Project is running at http://localhost:8080/
i 「wds」: webpack output is served from /dist/
i 「wdm」: wait until bundle finished: /dist/main.css
i 「wdm」: wait until bundle finished: /dist/main.js
D:\demo\node_modules\webpack\lib\Chunk.js:846
                throw new Error(
                ^

Error: Chunk.entrypoints: Use Chunks.groupsIterable and filter by instanceof Entrypoint instead
    at Chunk.get (D:\demo\node_modules\webpack\lib\Chunk.js:846:9)
    at D:\demo\node_modules\extract-text-webpack-plugin\dist\index.js:176:48
    at Array.forEach (<anonymous>)
    at D:\demo\node_modules\extract-text-webpack-plugin\dist\index.js:171:18
    at AsyncSeriesHook.eval [as callAsync] (eval at create (D:\demo\node_modules\tapable\lib\HookCodeFactory.js:32:10), <anonymous>:7:1)
    at AsyncSeriesHook.lazyCompileHook (D:\demo\node_modules\tapable\lib\Hook.js:154:20)

说明你用的是webpack 4以上的版本,4以上的版本不支持extract-text-webpack-plugin这个插件

解决办法:mini-css-extract-plugin这个插件代替,安装

18 使用webpack构建vue项目时可以使用一种新的构建模式.vue单文件组件

.vue单文件组件:一个后缀名为.vue的文件,在webpack中使用vue-loader就可以对这种文件进行处理:一个.vue文件包含三部分tempale,script,style

按照如下所示顺序安装来使用.vue文件

npm install --save vue

npm install --save-dev vue-loader

npm install --save-dev vue-style-loader

npm install --save-dev vue-template-compiler

npm install --save-dev vue-hot-reload-api

npm install --save-dev babel

npm install --save-dev babel-loader

npm install --save-dev babel-core

npm install --save-dev babel-plugin-transform-runtime

npm install --save-dev babel-preset-es2015

npm install --save-dev babel-runtime

19 安装完成后修改webpack.config.js来支持vue文件和ES6的解析

            {
                test:/\.css$/,
                use:[
                        {
                            loader:extractTextPlugin.loader,
                            options:{

                            }
                        },
                        ''css-loader''
                    ]
            },

            {
                test:''/\.js$/'',
                loader:''babel-loader'',
                exclude:/node_modules/
            },

            {
                test:''/\.vue$/'',
                loader:''vue-loader'',
            }

20 在demo目录下新建一个名为.babelrc的文件,并写入babel的配置,webpack会依赖此配置文件来使用babel编译es6代码

{

    "presets":["es2015"],
    "plugins":["transform-runtime"],
    "commments":false
}

 21 配置好这些就可以使用vue文件了

React 基础实践教程(一):搭建简单的 React + ES6/7 + Webpack 开发环境

React 基础实践教程(一):搭建简单的 React + ES6/7 + Webpack 开发环境

React 虽然是个很简单的 UI 库,API 也很少,但是在实际项目中却发现要搭配各种技术栈,例如 Webpack, Babel, Redux, React Router, CSS Modules, NPM, ES6/7 等等,仿佛怎么也学不完,很多人开始吐槽前端发展混乱,例如文章:2016 年学 JavaScript 是一种什么样的体验?吐槽归吐槽,扩充自己的技术栈,了解国外前端都在玩什么,毕竟不是坏事。

本系列教程不需要预先了解太多原理,跟着做,实践之后不懂再去查看文档,更有利于入门。

TIP: NPM 下载慢可以切换到淘宝源:NPM 切换淘宝源

1. 首先,建立项目目录,npm init 初始化 npm 项目

mkdir react-start
cd react-start
npm init

2. 全局安装 Webpack, Babel, Webpack-dev-server

sudo npm install babel webpack webpack-dev-server -g

3. 安装 react, react-dom

npm install react react-dom --save

4. 安装 Babel 转换器,需要用到插件 babel-preset-react, babel-preset-latest,latest 即最新的 ES 规范,包括了 Async/Await 这些新特性。

npm install babel-loader babel-core babel-preset-react babel-preset-latest --save

5. 创建项目文件,main.js 即项目入口文件,App.js 即 React 组件主文件

touch index.html App.js main.js webpack.config.js

图片描述

6. Webpack 配置

module.exports = {
    entry: ''./main.js'', // 入口文件路径
    output: {
        path: ''./'',
        filename: ''index.js''
    },
    devServer: {
        inline: true,
        port: 3333
    },
    module: {
        loaders: [
            {
                test: /\.js$/, // babel 转换为兼容性的 js
                exclude: /node_modules/,
                loader: ''babel'',
                query: {
                    presets: [''react'', ''latest'']
                }
            }
        ]
    }
}

6. 其他文件内容,一些基本的 React 和 ES6 基础,不做过多讲解了。

index.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>React Start</title>
    </head>
    <body>
        <div id="app"></div>
        <script src="index.js"></script>
    </body>
</html>

App.js

import React from ''react'';

class App extends React.Component {

    render() {
        return <div>Hello World</div>
    }
}

export default App

main.js

import React from ''react'';
import ReactDOM from ''react-dom'';
import App from ''./App'';

ReactDOM.render(<App />, document.getElementById(''app''))

7. 配置 npm scripts, 编辑 package.json

"scripts": {
   "start": "webpack-dev-server"
},

8. 基本的框架搭建完毕,npm start 然后打开 http://localhost:3333 试试

npm start

React+webpack搭建前端开发环境(一)

React+webpack搭建前端开发环境(一)

申明:本文定有诸多不足之处,欢迎指正,希望有兴趣的同学一起学习讨论!使用webpack打包web项目:(请先安装node.js,visual studio code代码编辑器 )

1. 在任意目录下创建项目的文件夹;我的 F:\study\photoAlbum
2. 在该目录下启动cmd命令行工具;使用shift+右键->在该目录下启动cmd
3.  npm init 
    a. 下来填写相应信息,
    b. 必要字段填,不必要字段回车即可
    c. 最后敲入yes ;生成package.json
    d. Package.json的内容大致如下;


4. 接下在该目录下新建项目的主要目录我的目录大概如下

看到没,package.json就是刚刚生成的,
    App文件夹为打包后的输出目录,这里边的文件不需要我们手动创建,全部是配置好之后webpack输出的;
    client用于存放前端代码;
    Server后边可能用于存放模拟后台的代码

5. 接下来我们要用npm在该项目中安装一些模块,这些模块在开发中要用到,比如一般项目中:
webpack, babel, style-loader,css-loader file-loader, html-webpack-plugin,url-loader, webpack-dev-serve, 其他如果你要用react编写程序,那么还得安装下边这些东西:react,react-dom;react-redux,react-router-dom,redux,styled-componentd等等,这些是开发中要使用的基本的安装包,具体在后续开发使用到什么就安装什么,
使用: npm install webpack –save(可以使用该命令一个一个安装,也可以全写在package.json中,然后执行npm install命令安装)(如果npm安装太慢,可使用淘宝镜像cnpm安装,可以去搜一波它的安装与使用,基本与npm相同)
Ok 如下图,我先安装了一个,注意package.json中的变化;

然后在该目录下多出一个node-moudels文件夹,里边就是安装的这些模块的鬼东西;

6. 接下来在photoAlbum目录下新建webpack.config.js;怎么说呢,这个文件就是告诉webpack它应该做的事情;该文件的内容大致如下:可以直接抄写,下边是图片内容(注意:一张图片放不下,用了两张图片。注意抄对)


看到没:上边就用到了我们在第五步中安装的模块webpack,html-webpack-plugin等;
关于webpack.config.js的配置学习一个超给力的学习地址:
https://doc.webpack-china.org/configuration/dev-server/
7. 在第六步中有两个要注意的地方:index.js和index.html;
js文件是入口文件必须的,html是webpack打包中必须的文件(相当于提供一个容器);
所以在client文件夹中,新建如下文件,

他们的内容大致如下:


就是这么简单
8. 在package.json文件中添加如下命令:

让项目使用webpack打包,且在8080默认端口跑起来;
9. 在cmd命令窗口输入 npm run build 回车打包;
编译成功


运行npm run dev

运行成功:

在页面输入http://localhost:8080/:

之后可以在index.js中开始编写前端代码,例如,加入路由,登录界面,主界面等;具体看下回分解;

今天关于react入门之使用webpack搭配环境的介绍到此结束,谢谢您的阅读,有关(24/24) webpack小案例--自己动手用webpack构建一个React的开发环境、1 使用webpack搭建vue开发环境、React 基础实践教程(一):搭建简单的 React + ES6/7 + Webpack 开发环境、React+webpack搭建前端开发环境(一)等更多相关知识的信息可以在本站进行查询。

如果您对reactjs – 如何使用React和Webpack设置Babel 6 stage 0react配置webpack感兴趣,那么这篇文章一定是您不可错过的。我们将详细讲解reactjs – 如何使用React和Webpack设置Babel 6 stage 0的各种细节,并对react配置webpack进行深入的分析,此外还有关于babel - 使用Webpack和Babel来搭建React应用程序、react+webpack+babel+webpcak-dev-server+react-router-dom从无到有、React+Webpack+Eslint+Babel构建React脚手架、reactjs – IE11中的Webpack,Babel和React语法错误的实用技巧。

本文目录一览:

reactjs – 如何使用React和Webpack设置Babel 6 stage 0(react配置webpack)

reactjs – 如何使用React和Webpack设置Babel 6 stage 0(react配置webpack)

我从文档的理解

我看到Babel 6现在有三个预设:es2015,反应和stage-x。
我读了,我可以设置在.babelrc像这样:

{
  "presets": ["es2015","react","stage-0"]
}

或直接在package.JSON像这样:

{
  ...,"version": x.x.x,"babel": {
    "presets": ["es2015","stage-0"]
  },...,}

我可以进一步使用babel-loader与webpack这样:

loader: 'babel?presets[]=es2015'

我的问题

所以编译一切不错,干净我添加babel-loader,它刚刚更新使用Babel6,到webpack配置像这样:

module.exports = function(options) {
  var jsLoaders = ['babel?presets[]=es2015'];
  [...]
    loaders: [
      {
        test: /\.js$/,exclude: /node_modules/,loaders: jsLoaders
      },{
        test: /\.jsx$/,loaders: options.production ? jsLoaders : ['react-hot'].concat(jsLoaders)
      },[...]

现在当我编译没有.babelrc在根或预设选项设置package.JSON,即只有在webpack配置中的babel-loader es2015预设置我得到一个意想不到的令牌错误关于静态propTypes在我的React组件类:

ERROR in ./app/components/form/index.jsx
Module build Failed: SyntaxError: /Library/WebServer/Documents/yarsk.test/app/components/form/index.jsx: Unexpected token (19:19)
  17 | // ES6 React Component:
  18 | export default class SurveyForm extends Component {
> 19 |   static propTypes = {
     |                    ^

在GitHub我被告知这是一个阶段1的功能,即变换类的属性。所以我想立即实现stage-0。

当我这样做通过添加.babelrc或定义package.JSON像上面我得到一个非常奇怪的构建失败错误:

ERROR in ./app/components/form/index.jsx
Module build Failed: Error: /Library/WebServer/Documents/yarsk.test/app/components/form/index.jsx: We don't kNow what to do with this node type. We were prevIoUsly a Statement but we can't fit in here?
    at NodePath.insertAfter (/Library/WebServer/Documents/yarsk.test/node_modules/babel-traverse/lib/path/modification.js:181:13)
    at NodePath.replaceWithMultiple (/Library/WebServer/Documents/yarsk.test/node_modules/babel-traverse/lib/path/replacement.js:92:8)
    at handleClassWithSuper (/Library/WebServer/Documents/yarsk.test/node_modules/babel-plugin-transform-class-constructor-call/lib/index.js:80:10)
    at PluginPass.Class (/Library/WebServer/Documents/yarsk.test/node_modules/babel-plugin-transform-class-constructor-call/lib/index.js:101:11)
    at newFn (/Library/WebServer/Documents/yarsk.test/node_modules/babel-traverse/lib/visitors.js:233:27)
    at NodePath._call (/Library/WebServer/Documents/yarsk.test/node_modules/babel-traverse/lib/path/context.js:72:18)
    at NodePath.call (/Library/WebServer/Documents/yarsk.test/node_modules/babel-traverse/lib/path/context.js:44:17)
    at NodePath.visit (/Library/WebServer/Documents/yarsk.test/node_modules/babel-traverse/lib/path/context.js:102:12)
    at TraversalContext.visitQueue (/Library/WebServer/Documents/yarsk.test/node_modules/babel-traverse/lib/context.js:151:16)
    at TraversalContext.visitSingle (/Library/WebServer/Documents/yarsk.test/node_modules/babel-traverse/lib/context.js:111:19)
    at TraversalContext.visit (/Library/WebServer/Documents/yarsk.test/node_modules/babel-traverse/lib/context.js:195:19)
    at Function.traverse.node (/Library/WebServer/Documents/yarsk.test/node_modules/babel-traverse/lib/index.js:139:17)
    at NodePath.visit (/Library/WebServer/Documents/yarsk.test/node_modules/babel-traverse/lib/path/context.js:106:22)
    at TraversalContext.visitQueue (/Library/WebServer/Documents/yarsk.test/node_modules/babel-traverse/lib/context.js:151:16)
    at TraversalContext.visitMultiple (/Library/WebServer/Documents/yarsk.test/node_modules/babel-traverse/lib/context.js:106:17)
    at TraversalContext.visit (/Library/WebServer/Documents/yarsk.test/node_modules/babel-traverse/lib/context.js:193:19)
    at Function.traverse.node (/Library/WebServer/Documents/yarsk.test/node_modules/babel-traverse/lib/index.js:139:17)
 @ ./app/index.jsx 9:0-28

或者简而言之:模块构建失败:错误:/…/index.jsx:我们不知道该做什么这个节点类型。我们以前是一个声明,但我们不能在这里?

这是我被困住的地方。我用Babel5编写这个组件,当我能够像这样用babel-loader编译时,一切工作正常:

loader: 'babel?optional[]=runtime&stage=0

现在我得到提到的错误编译。

>这是一个babel-loader问题,还是babel问题?
>我在哪里必须配置stage-0,使它不会
抛出错误?

更新

当使用预设集编译并使用所提到的类导出错误的解决方法(在创建它之前不能导出类​​),设置预设的顺序会更改错误消息。当我设置stage-0首先错误现在是’this’不允许之前super()(这是内部节点上的错误。可能是内部错误)
当我把stage-0第二或第三我从上面得到关于语法错误的消息。

最新

关于这些bug的最新进展see my post或the new babel issue tracker on phabricator更多。 (基本上编译固定为6.2.1,但现在还有其他事情发生)

本文中提到的所有错误都是从Babel 6.3.x完全修复的。如果您仍遇到问题,请更新您的依赖关系。

我在这里遇到的两个相当重的bug,即直接导出带有静态属性的ES6类和ES6构造函数的问题在这个线程的答案中讨论,可以在GitHub上显式地找到(导出bug)和这里(constructor bug)。 (GitHub问题跟踪器已关闭, issues,bugs and requests have moved here)

这些都是正式确认的错误,从Babel 6.3.17以来修复

(也许一两个更早,不是在6.3.x之前,这是我在的版本,一切正在工作,因为它是与Babel5。快乐编码大家。)

(对于记录:)

因此,如果您在CLI中收到以下错误消息:

我们不知道该怎么做这个节点类型。我们以前是一个声明,但我们不能在这里?

您可能正在导出具有此类静态属性或类似方式的ES6类(请注意,这似乎没有连接到正在扩展的类,而是连接到具有静态属性的类):

import React,{ Component,PropTypes } from 'react'

export default class ClassName extends Component {
  static propTypes = {...}
  // This will not get compiled correctly for Now,as of Babel 6.1.4
}

简单的解决方法as mentioned by Stryzhevskyi和几个人在GitHub上:

import React,PropTypes } from 'react'

class ClassName extends Component {
  static propTypes = {...}
}
export default ClassName // Just export the class after creating it

第二个问题是关于以下错误:

‘this’不允许在super()之前(这是内部节点上的错误,可能是内部错误)

尽管是一个合法的规则as pointed out by Dominic Tobias这是一个已确认的错误,其中似乎扩展类有自己的属性将抛出这个或类似的消息。至于现在我还没有看到这一个的任何解决方法。很多人回到Babel5为这个原因现在(从6.1.4)。

据说这是固定的释放Babel 6.1.18(见上面的GitHub问题),但人,我包括,仍然看到同样的确切问题发生。

还要注意,现在,您加载babel预设stage-x,反应和es2015的顺序似乎很重要,可能会改变您的输出。

至于Babel 6.2.1

这两个bug都是固定的,代码编译得很好。但是还有另一个可能影响很多人使用反应,抛出ReferenceError:这没有被初始化 – super()没有在运行时被调用。敬请关注…

从Babel 6.3.17完全修复

(也许一两个更早,不是在6.3.x之前,这是我在的版本,一切正在工作,因为它是与Babel5。快乐编码大家。)

babel - 使用Webpack和Babel来搭建React应用程序

babel - 使用Webpack和Babel来搭建React应用程序

用Webpack(npm install -g webpack)代码打包,Webpack大致需要知道三件事:


1)让Webpack知道应用程序或js文件的根目录
2)让Webpack知道做何种转换
3)让Webpack知道转换后的文件保存在哪里

具体来说,大致要做以下几件事情:

1)在项目根目录下有一个webpack.config.js文件,这个是惯例
2)确保webpack.config.js能导出一个对象


module.exports = {};

3)告诉Webpack入口js文件在哪里

module.exports = { entry: ['./app/index.js'] }

4)告诉Webpack需要哪些转换插件

'./app/index.js'],module: { loaders: [] } }

所有的转换插件放在loaders数组中。

5)设置转换插件的细节

module: { loaders: [ { test: /\.coffee$/,include: __dirname + 'app',loader: "coffee-loader" } ] } }

  • test:运行.coffee结尾的文件
  • include:哪些文件件
  • loader:具体的转换插件

6)告诉Webpack导出到哪里

"coffee-loader" } ] },output: { filename: "index_bundle.js",path: __dirname + '/dist' } }

【文件结构】

app/
.....components/
.....containers/
.....config/
.....utils/
.....index.js
.....index.html
dist/
.....index.html
.....index_bundle.js
package.json
webpack.config.js
.gitignore

我们不禁要问,如何保证app/index.html和dist/index.html同步呢?

如果,我们每次运行webpack命令后,把app/index.html拷贝到dist/index.html中就好了。

解决这个问题的人是:html-webpack-plugin(npm install --save-dev html-webpack-plugin)。

引入html-webpack-plugin之后,webpack.config.js看起来是这样的:

var HtmlWebpackPlugin = require('html-webpack-plugin');
var HtmlWebpackPluginConfig = new HtmlWebpackPlugin({
    template: __dirname + '/app/index.html',filename: 'index.html',inject: 'body'
});

'/dist'
    },plugins: [HTMLWebpackPluginConfig]
}
  • template: 表示源文件来自
  • filename:目标文件的名称
  • inject: 'body'表示把对dist/index_bundle.js文件的引用,放到目标文件dist/index.html的body部分

【Webpack的一些指令】

webpack webpack -w //监测文件变化 webpack -p //不仅包括转换,还包括最小化文件

【Babel】

Babel可以用来把JSX文件转换成js文件。与Babel相关的安装包括:

npm install --save-dev babel-core npm install --save-dev babel-loader npm install --save-dev babel-preset-react

  • babel-core: 就是babel本身
  • babel-loader:用来加载
  • babel-preset-react:用来完成JSX到JS的文件转换

在项目根文件夹下创建.babelrc文件。

{
    "presets": ["react"]
}

把babel-loader放到webpack.config.js文件的设置中去。

module: {
        loaders: [
            {
                test: /\.js$/,21); line-height:1.8">"babel-loader"
            }
        ]
    },plugins: [HTMLWebpackPluginConfig]
}


参考:http://tylermcginnis.com/react-js-tutorial-1-5-utilizing-webpack-and-babel-to-build-a-react-js-app/ 

react+webpack+babel+webpcak-dev-server+react-router-dom从无到有

react+webpack+babel+webpcak-dev-server+react-router-dom从无到有

react-demo

两个目标:

  • 手动搭建react脚手架:react、bable、webpack、react-router-dom、webpack-dev-server
  • 理解webpack从无到有打包原理

手动搭建react脚手架项目地址

有坑的地方会特别说明,因为好多文章都没有给出babel和webpack-dev-server的版本,所以在搭建过程中遇到比较多的问题。

1.react react react-dom
2.babel babel-loader babel-core babel-presets-es2015|stage-0|react

babel 这里的坑:注意babel的版本号,比如运行时报错babel/core找不到等等,需要降低babel的版本,

注意webpack.config.js配置文件中的loader书写,需要从webpack官网找,有的博客写的不对会造成错误

注意需要有一个.babelrc的文件

3.webpack
4.webpack-dev-server

使用webpack-dev-server --hot --inline --config webpack.config.js 运行报错,注意降低webpack-dev-server的版本号

5.react-router-dom

React+Webpack+Eslint+Babel构建React脚手架

React+Webpack+Eslint+Babel构建React脚手架

React+webpack+Eslint+Babel构建React脚手架

参考网上文章,说的不是很全,想自己写一篇来巩固知识点,脚手架源码参考阮一峰老师的Github

所用技术栈

  • React

  • Babel

  • Webpack

  • Eslint

  • travis

  • ES6

构建过程

安装nodejs

初始化项目:

npm init -y        注:-y的意思是默认安装

目录构建

配置package.json

npm初始化后会自动生成

添加:

"dependencies": {
    "babel-runtime": "6.x","react": "15.x","react-dom": "15.x"
  },"devDependencies": {
    "babel-core": "6.x","babel-eslint": "7.x","babel-loader": "6.x","babel-plugin-transform-runtime": "6.x","babel-preset-es2015": "6.x","babel-preset-react": "6.x","babel-preset-stage-0": "6.x","copy-webpack-plugin": "latest","css-loader": "~0.26.1","eslint": "latest","eslint-config-airbnb": "latest","eslint-formatter-pretty": "^1.1.0","eslint-plugin-compat": "^1.0.0","eslint-plugin-import": "latest","eslint-plugin-jsx-a11y": "3.x","eslint-plugin-promise": "^3.4.0","eslint-plugin-react": "latest","open-browser-webpack-plugin": "0.0.3","style-loader": "~0.13.1","webpack": "1.x","webpack-dev-server": "1.x"
  }

或者在命令行中使用安装命令,加深印象。注:-S是安装在生产环境,-D安装在开发环境。

//安装react
npm install react -S
npm install react-dom -S

//减少打包的时候重复代码
npm install babel-runtime  -S
npm install babel-plugin-transform-runtime -D

//安装babel相关
npm install babel-loader -D //安装babel-loader
npm install babel-core -D //安装babel核心
npm install babel-preset-es2015 -D //支持ES2015
npm install babel-preset-react -D  //支持jsx
npm install babel-preset-stage-0 -D //支持ES7
npm install babel-eslint -D 

//安装webpack
npm install webpack -D //模块管理和打包工具
npm install webpack-dev-server -D //监听代码自动刷新

//安装Eslint相关
npm install eslint -D
npm install eslint-config-airbnb -D
npm install eslint-formatter-pretty -D
npm install eslint-plugin-compat -D
npm install eslint-plugin-import -D
npm install eslint-plugin-jsx-a11y -D
npm install eslint-plugin-promise -D
npm install eslint-plugin-react -D

配置webpack.config.js

Webpack将项目中的所有静态资源都当做模块,模块之间可以互相依赖,由webpack对它们进行统一的管理和打包发布。

安装webpack后,手动创建文件进行定制。

webpack.production.config.js与之类似。

const webpack = require('webpack');
const path = require('path');
const OpenbrowserPlugin = require('open-browser-webpack-plugin');

module.exports = {
  devServer: {
    historyApiFallback: true,hot: true,inline: true,progress: true,contentBase: './app',port: 8080
  },entry: [
    'webpack/hot/dev-server','webpack-dev-server/client?http://localhost:8080',path.resolve(__dirname,'app/main.jsx')
  ],output: {
    path: path.resolve(__dirname,'build'),publicPath: '/',filename: './bundle.js'
  },module: {
    loaders: [
      { test: /\.css$/,include: path.resolve(__dirname,'app'),loader: 'style-loader!css-loader' },{ test: /\.js[x]?$/,exclude: /node_modules/,loader: 'babel-loader' }
    ]
  },resolve: {
    extensions: ['','.js','.jsx']
  },plugins: [
    new webpack.HotModuleReplacementPlugin(),new OpenbrowserPlugin({ url: 'http://localhost:8080' })
  ]
};

配置.babelrc

babel是ES2015 语法转化器,可从Babel学习其用法。

安装babel后,手动创建进行配置。

{
  "presets": [ "es2015","stage-0","react"],"env": {
    "build": {
      "optional": ["optimisation","minification"]
    }
  }
}

配置.eslintrc

ESLint是一个QA工具,用来避免低级错误和统一代码的风格。

安装eslint后,手动创建进行配置。

{
  "parser": "babel-eslint","extends": "airbnb","env": {
    "browser": true,"node": true
  },"parserOptions": {
     "ecmaVersion": 6,"sourceType": "module","ecmaFeatures": {
       "jsx": true
     }
  },"globals": {
    "window": true,"document": true
  },"rules": {
    "arrow-parens": 0,"class-methods-use-this": 0,"compat/compat": 2,"comma-dangle": 0,"consistent-return": 2,"func-names": 2,"generator-star-spacing": [0],"import/no-extraneous-dependencies": ["off"],"import/extensions": 0,"import/no-unresolved": 2,"new-cap": 0,"no-implicit-coercion": "error","no-mixed-operators": 0,"no-plusplus": 0,"no-use-before-define": 0,"no-nested-ternary": 0,"no-underscore-dangle": 0,"no-var": "error","semi": ["error","always"],"promise/param-names": 2,"promise/always-return": 2,"promise/catch-or-return": 2,"promise/no-native": 0
  },"plugins": [
    "compat","import","promise"
  ]
}

配置.travis.yml

Travis Ci是一个基于云的持续集成项目,目前已经支持大部分主流语言了,如:C、PHP、Ruby、Python、Nodejs、Java、Objective-C等等,Travis Ci与Github集成非常紧密,官方的集成测试托管只支持Github项目,不过你也可以通过Travis Ci开源项目搭建一套属于自己的方案。

可从Travis注册使用,很方便。

sudo: false

language: node_js
node_js:
  - "node"

最后贴上自己的Github,前端小白,欢迎指导。

reactjs – IE11中的Webpack,Babel和React语法错误

reactjs – IE11中的Webpack,Babel和React语法错误

我在Internet Explorer 11中的React Redux项目中遇到语法错误,但我不知道为什么会导致它.

我正在使用Webpack和Babel来编译它.

我尝试使用babel-polyfill和babel-es6-polyfill,但这没有帮助.

这是我得到的错误:

SCRIPT1002: Syntax error
File: app.js,Line: 70,Column: 1

第70行第1列是Webpack的eval开始的地方:

/***/ }),/* 21 */,/* 22 */
/***/ (function(module,exports,__webpack_require__) {

"use strict";
eval("\n\nObject.define... <- Line 70
^--- Column 1

这是我的webpack.config.js:

'use strict';
// Include modules and plugins
const webpack = require('webpack');
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');

// App and build directories
const APP_DIR = path.resolve(__dirname,'src/');
const BUILD_DIR = path.resolve(__dirname,'public');

// Extract sass from the application,see index.jsx
const extractSass = new ExtractTextPlugin({
    filename: 'css/[name].css'
});

// The config file to load
let env = (process.env.NODE_ENV || 'dev').toLowerCase();
let configFile = path.resolve(__dirname,'config/config.' + env + '.json');

// Default config file if not found
const defaultConfigFile = path.resolve(__dirname,'config/config.dev.json');

/*
 * Config to be injected into the app
 * Note that JSON files are parsed upon requiring
 */
let config;

/*
 * Get the actual config
 */
try {
    config = require(configFile);
    console.log('Loaded config file ' + configFile);
} catch (e) {
    config = require(defaultConfigFile);
    console.log('Fallen back to default config file');
}

// The actual webpack config
const webpackConfig = {
    entry: {
        // The app entry point
        app: APP_DIR + '/index.jsx',// vendor files will be used for bundling,they will not be compiled into the app itself
        vendor: [
            'axios','prop-types','react','reactstrap','react-chartjs-2','react-dom','react-redux','react-router','react-router-dom','redux','sprintf-js',]
    },output: {
        path: BUILD_DIR,filename: 'js/app.js'
    },module: {

        /*
         * These are loaders for webpack,these will assist with compilation
         */
        loaders: [
            {
                /*
                 * Use Babel to compile JS and JSX files
                 * See .babelrc
                 */
                test: /\.jsx?/,include: APP_DIR,loader: 'babel-loader'
            }
        ],rules: [
            {
                /*
                 * Sass/Scss compilation rules
                 */
                test: /\.scss$/,use: extractSass.extract({
                    use: [
                        {
                            loader: 'css-loader'
                        },{
                            loader: 'sass-loader'
                        }
                    ],fallback: 'style-loader'
                })
            },{
                /*
                 * JS(X) compilation rules
                 * We need this,otherwise Webpack will crash during compile time
                 */
                test: /\.jsx?/,loader: 'babel-loader'
            }
        ]
    },plugins: [
        /*
         * The CommonsChunkPlugin is responsible to create bundles out of commonly used modules
          * E.g. React,React Dom,etc
         */
        new webpack.optimize.CommonsChunkPlugin({
            name: 'vendor',// See entry.vendor
            filename: 'js/vendor.bundle.js'
        }),extractSass
    ],externals: {
        /*
         * The config external will be available to the app by using require('config')
         */
        'config': JSON.stringify(config)
    },devServer: {
        contentBase: BUILD_DIR,compress: true,port: 7600,inline: true,},};

if (env === 'production') {
    webpackConfig.devtool = 'hidden-source-map';
} else {
    webpackConfig.devtool = 'eval-source-map';
}

module.exports = webpackConfig;

我的依赖项:

"dependencies": {
  "axios": "^0.16.1","babel-core": "^6.24.0","babel-loader": "^6.4.1","babel-polyfill": "6.5.1","babel-preset-es2015": "^6.24.0","babel-preset-react": "^6.23.0","babel-preset-stage-1": "^6.24.1","chart.js": "^2.6.0","cross-env": "^3.2.4","css-loader": "^0.27.3","enumify": "^1.0.4","extract-text-webpack-plugin": "^2.1.0","history": "^4.6.3","ip": "^1.1.5","lodash": "^4.17.4","moment": "^2.18.1","node-sass": "^4.5.1","prop-types": "^15.5.10","react": "^15.4.2","react-addons-css-transition-group": "^15.5.2","react-addons-transition-group": "^15.5.2","react-chartjs-2": "^2.1.0","react-dom": "^15.4.2","react-js-pagination": "^2.1.0","react-redux": "^5.0.4","react-router": "^4.1.1","react-router-dom": "^4.1.1","reactstrap": "^4.5.0","redux": "^3.6.0","sass-loader": "^6.0.3","sprintf-js": "^1.1.0","style-loader": "^0.16.0","webpack": "^2.3.2"
},"devDependencies": {
  "eslint-plugin-react": "^6.10.3","webpack-dev-server": "^2.5.1"
}

我的.babelrc:

{
   "presets" : [
      "es2015","react","stage-1"
   ]
}

编辑1

按照BANANENMANNFRAU的回答,我添加了babel-preset-env并编辑了我的.babelrc如下:

{
   "presets" : [
      [ "env",{
         "targets": {
            "browsers": [
               "last 5 versions","ie >= 11"
            ]
         }
      }],"es2015","stage-1"
   ]
}

这没有帮助,它仍然导致IE11中的错误.

使用npm install babel-preset-env –save-dev安装 babel-preset-env并在.babelrc中使用以下配置:
{
  "presets" : [
    ["env",{
      "targets": {
        "browsers": ["last 2 versions","ie >= 11"]
      }
    }],]
}

您还可以从配置中删除以下部分:

loaders: [
            {
                /*
                 * Use Babel to compile JS and JSX files
                 * See .babelrc
                 */
                test: /\.jsx?/,

检查文档here

今天关于reactjs – 如何使用React和Webpack设置Babel 6 stage 0react配置webpack的分享就到这里,希望大家有所收获,若想了解更多关于babel - 使用Webpack和Babel来搭建React应用程序、react+webpack+babel+webpcak-dev-server+react-router-dom从无到有、React+Webpack+Eslint+Babel构建React脚手架、reactjs – IE11中的Webpack,Babel和React语法错误等相关知识,可以在本站进行查询。

以上就是给各位分享React Redux 中间件思想遇见 Web Worker 的灵感,其中也会对附demo进行解释,同时本文还将给你拓展React 从入门到放弃 (4):Redux 中间件、React 项目中Redux 中间件的理解、react+react-redux demo 连载 如何自学前端、react+react-router+redux+Node.js+socket.io写一个聊天webapp等相关知识,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!

本文目录一览:

React Redux 中间件思想遇见 Web Worker 的灵感(附demo)(react中间件的作用)

React Redux 中间件思想遇见 Web Worker 的灵感(附demo)(react中间件的作用)

写在最前

原文首发于作者的知乎专栏:React Redux 中间件思想遇见 Web Worker 的灵感(附demo),感兴趣的同学可以知乎关注,进行交流。

熟悉 React 技术栈的同学,想必对 Redux 数据流框架并不陌生。其倡导的单向数据流等思想独树一帜,虽然样板代码会有一定程度上的增多,但是对于开发效率和调试效率的提高是显著的。同时还带来了很多诸如 “时间旅行”,“ undo/redo ” 等黑魔法。

其实这还只是表象。如果你深入去了解 Redux 的设计理念,探索中间件奥秘,玩转高阶 reducer 等等,迎接你的就会是另一扇门。透过它,函数式编程思想之光倾斜如注。

思想背景

但是随着这个 web app 复杂度的提升,数据计算量压力徒增,你所设计的 Reducer 变得臃肿不堪。好吧,我们可以拆分 Reducer 使得代码看上去更加舒服。可是计算量呢?也许有一些“梦魇”,瓶颈般永远无法消除。

冥冥之中,“各种处理计算既然注定在同一时空,那么能否永远平行?”

曾几何时,你是否听说过 JS 单线程异步?听说过浏览器卡顿或卡死?听说过 60 fps?

其实一个很严峻的事实是:根据 60 fps 计算,每一帧留给我们 JS 执行的时间为 16ms(甚至更少)。那么一旦当 Reducer 计算时间过长,必然会影响浏览器渲染。

多线程思路

关于浏览器主线程、render queue、event loop、call stack 等内容,本文不再复述,因为里面的知识完全都够写一本书了。假定读者对其有一二认知,那么你也不难理解我们即将登场的救星—— Web Worker!

我们先来简单认识一下 web worker:

2008 年 W3C 制定出第一个 HTML5 草案开始,HTML5 承载了越来越多崭新的特性和功能。其中,最重要的一个便是对多线程的支持。在 HTML5 中提出了工作线程(Web Worker)的概念,并且规范出 Web Worker 的三大主要特征:

  • 能够长时间运行(响应);

  • 理想的启动性能;

  • 以及理想的内存消耗。

Work 线程可以执行任务而不干扰用户界面。

于是,脑洞大开,能否将我们的 Redux Reducer 计算状态部分放进 Worker 线程中处理呢?

答案是肯定的。
那么要如何实施呢?

我们先来看一下经典的 Redux workflow,如下图:

如果要接入 Web Work,那么我们改动流程图如下:

具体实现和一个demo

当然,有了思路,还需要在实战中演练。

我使用 “N-皇后问题” 模拟大型计算,并且实现的 demo 中可以任意设置 n 值,增加计算耗时。
如果你不理解此算法也没有关系,只需要知道N-皇后问题这个算法的计算耗时很长,且和 n 值相关:n 越大,计算成本越大。

除了一个极其耗时的计算,页面中还运行这么几个模块,来实现复杂的渲染逻辑操作:

  • 一个实时每16毫秒,显示计数(每秒增加1)的 blinker 模块;

  • 一个定时每500毫秒,更新背景颜色的 counter 模块;

  • 一个永久往复运动的 slider 模块;

  • 一个每16毫秒翻转5度的 spinner 模块

这些模块都定时频繁地更新 dom 样式,进行大量复杂的渲染计算。正常情况下,由于 JS 主线程进行N-皇后计算,这些渲染过程都将被卡顿。

同时,我设置“N-皇后问题”的 n 值,来观察在计算时这些模块的表现(是否卡顿)。在不开启 Work 线程的情况下,n 设置为13时,有 gif 图,左半部分:

我们非常清晰地看到:由于浏览器 call stack 进行 n=13 的皇后问题计算,而无法“按时”渲染,所以造成了这几个模块的卡顿,这些模块都无法更新状态。在这个卡顿过程中,用户的任何事件(如点击,敲键盘等)都无法被浏览器响应。这就是用户体会到的“慢”!

如果我把 n 值设置的大与13呢,比如24?
千万不要这么做!因为你的浏览器会被卡死!我使用 Mac Pro 8G 内存情况下,设置到14,浏览器就无法响应了。

在开启 Work 线程时,请参考上 gif 图右半部分,几个模块的渲染丝毫不受影响。完美达到了我们的目的。

因为 Reducer 的超级耗时计算被放入 Worker 线程当中,所以丝毫没有影响浏览器的渲染和响应。完全解决了用户觉得“电脑慢”的问题。

看到了如此完美的对比,也许你想问 Web Worker 的兼容性如何呢?

总结

其实,这篇文章的意义并不在于这个 demo 和应用。而是在启发一种新的想法的同时,review 了很多 JS 当中关键概念和基本知识。比如:单线程异步、宿主环境、60 fps、一个算法等等。

更值得一提的是,如果你去深入 demo 代码,你更会发现 Redux 设计精妙的思想,比如我们将 Web Worker 的应用抽象出一个公共库:Redux-Worker,并包装为 Redux 的中间件(middleware),所有 React Redux 都可以无侵入,采用中间件的思想使用:

import { applyWorker } from 'redux-worker';
const enhancerWithWorker = compose(
    applyMiddleware(thunk,logger),applyWorker(worker)
);

const store = createStore(rootReducer,{},enhancerWithWorker);

当然,Redux-Worker 这个中间件的实现原理更是巧妙,这里不再展开。感兴趣的同学可以参考我的此项目 Github 仓库。我 fork 了此库源码,并在核心逻辑加入了中文注释,感兴趣的同学可以关注。

我的其他关于 React 文章:

  • 通过实例,学习编写 React 组件的“最佳实践”

    • React 组件设计和分解思考

    • [从 React 绑定 this,看 JS 语言发展和框架设计]()

  • React 服务端渲染如此轻松 从零开始构建前后端应用

  • 做出Uber移动网页版还不够 极致性能打造才见真章

  • 解析Twitter前端架构 学习复杂场景数据设计

  • React Conf 2017 干货总结1: React + ES next = ♥

  • React+Redux打造“NEWS EARLY”单页应用 一个项目理解最前沿技术栈真谛

  • 一个react+redux工程实例

Happy Coding!

PS:
作者Github仓库 和 知乎问答链接欢迎各种形式交流。

React 从入门到放弃 (4):Redux 中间件

React 从入门到放弃 (4):Redux 中间件

redux 提供了类似后端 Express 的中间件概念。 最适合扩展的是 redux 中的 store.dispatch 方法,中间件实际就是通过 override redux 的 store.dispatch () 完成 将 action -> reducer 过程变为 action -> middlewares -> reducer 如:

let next = store.dispatch;
store.dispatch = function dispatchAndLog(action) {
  console.log(''dispatching'', action);
  next(action);
  console.log(''next state'', store.getState());
}

添加中间件

redux 提供了 applyMiddleware 方法便于添加中间件

applyMiddleware 的源码:

export default function applyMiddleware(...middlewares) {
  middlewares = middlewares.slice()
  middlewares.reverse()

  // Transform dispatch function with each middleware.
  middlewares.forEach(middleware =>
    // 由于每次middle会直接返回返回函数,然后在这里赋值给store.dispatch,
    // 下一个middle在一开始的时候,就可以通过store.dispatch拿到上一个dispatch函数
    store.dispatch = middleware(store)
  )
}

通过 middleware 将 store.dispatch 进行扩展

middleware 会返回一个函数:return store => dispatch => action => {}

异步操作

  1. 同步操作只要发出一种 Action 即可,异步操作通常需要发出多种 Action (开始、成功、失败)
  2. 异步操作有 2 种 Action 写法 (3 种 type 或者 添加 erro 和 response 字段)
  3. 异步操作的 state 结构调整

Action 写法:

// 写法一:名称相同,参数不同
{ type: ''FETCH_POSTS'' }
{ type: ''FETCH_POSTS'', status: ''error'', error: ''Oops'' }
{ type: ''FETCH_POSTS'', status: ''success'', response: { ... } }

// 写法二:名称不同
{ type: ''FETCH_POSTS_REQUEST'' }
{ type: ''FETCH_POSTS_FAILURE'', error: ''Oops'' }
{ type: ''FETCH_POSTS_SUCCESS'', response: { ... } }

异步 State 结构:

let state = {
  // ...
  isFetching: true,// 正在获取数据
  didInvalidate: true,// 是否过期
  lastUpdated: ''xxxxxxx''// 上次更新时间
};

使用中间件

  1. 使用 createStore(reducer, enhancer)createStore(reducer, preloadedState, enhancer)
  2. applyMiddleware 的参数为中间件,某些中间件有顺序要求如:logger

redux-logger

redux-logger 可清晰记录 preState action nextState time 等信息。

示例:

import { createStore, applyMiddleware } from ''redux''
import createLogger from ''redux-logger''
import rootReducer from ''./reducers''

let store = createStore(rootReducer, applyMiddleware(createLogger));

redux-thunk

redux-thunk 用来优化 redux 中的异步操作。

在 store.dispatch 的方法参数只支持 js 对象 (即 Action),使用 redux-thunk 将支持参数为一个函数。

或者说 redux-thunk 使得 action 从一个对象变成一个函数。

函数签名:(dispatch, getState) => {}

示例:

import { createStore, applyMiddleware } from ''redux''
import thunkMiddleware from ''redux-thunk''
import rootReducer from ''./reducers''

let store = createStore(rootReducer, applyMiddleware(thunkMiddleware));

store.dispatch( dispatch => {
    dispatch({type:''CLICK_START'',data:res})
    fetch(''xx'')
        .then(res => dispatch({type:''CLICK_END'',data:res}));
} )

实际上,redux-thunk 的作用是让 Action Creator 方便可以返回函数,这样让项目中的同步异步 Action Creator 调用可以保持一致

redux-saga

redux-saga 相比 thunk 功能显得全面,精细。 saga 是一个常驻进程,在复杂任务 及 长时事务场景非常适用。 这里通过 2 个 action 来展示 saga

示例:

import createSagaMiddleware, { delay } from ''redux-saga''
import { all, put, takeEvery, takeLatest } from ''redux-saga/effects''

function* helloSaga() {
    yield delay(1000)
    console.log(''hello world'')
}

function* incrementAsync() {
    yield delay(1000)
    yield put({ type: ''click'' })
}

function* rootSaga() {
    yield all([
        takeEvery(''hello'', helloSaga),
        takeLatest(''async'', incrementAsync)
    ])
}

let sagaMiddleware = createSagaMiddleware();

let store = createStore(rootReducer, applyMiddleware(sagaMiddleware))

sagaMiddleware.run(rootSaga);

store.dispatch({ type: ''hello'' });
store.dispatch({ type: ''async'' });

支持异步的还有 redux-promise 等

React 项目中Redux 中间件的理解

React 项目中Redux 中间件的理解

前言

React/Redux项目结束后,当我在研究react-router源码的时候发现当中有一部分含中间件的思想,所以才想把中间件重新梳理一遍;在之前看redux了解到中间件,redux层面中间件的理解对项目前期比较有帮助,虽然项目中后期基本可以忽略这层概念;现在对这部分的笔记重新梳理,这里只针对这个中间件做一个理解。

如果想学习项目的底层建设,建议先去学习官网redux案例,之后在学习react-router的使用

Redux 中间件介绍

Redux 目的是提供第三方插件的模式,改变action -> reducer 的过程。变为 action -> middlewares -> reducer 。自己在项目中使用它改变数据流,实现异步 action ;下面会对日志输出做一个开场。

使用 Redux 中间件

Redux 中 applyMiddleware 的方法,可以应用多个中间件,这里先只写一个中间件,以日志输出中间件为例

//利用中间件做打印log
import {createStore,applyMiddleware} from ''redux'';
import logger from ''../api/logger'';
import rootReducer from ''../reducer/rootReducer'';


let createStoreWithMiddleware = applyMiddleware(logger)(createStore);
let store = createStoreWithMiddleware(rootReducer);
// 也可以直接这样,可以参考createStore
// createStore(
//     rootReducer,
//     applyMiddleware(logger)
// )
export default store;

logger 中间件结构分析

const logger = store => next => action => {
    let result = next(action); // 返回的也是同样的action值
    console.log(''dispatch'', action);
    console.log(''nextState'', store.getState());
    return result;
};

export default logger;

store => next => action =>{} 实现了三层函数嵌套,最后返回 next ,给下一个中间件使用,接下来把三层函数拆解;

从applyMiddleware源码开始分析

///redux/src/applyMiddleware.js
export default function applyMiddleware(...middlewares) {
    return (createStore) => (reducer, initialState, enhancer) => {
        var store = createStore(reducer, initialState, enhancer)
        var dispatch = store.dispatch
        var chain = []
        var middlewareAPI = {
            getState: store.getState,
            dispatch: (action) => dispatch(action)
        }
        chain = middlewares.map(middleware => middleware(middlewareAPI))
        dispatch = compose(...chain)(store.dispatch)
        return {
            ...store,
            dispatch
        }
    }
}
最外层store
//源码分析
chain = middlewares.map(middleware => middleware(middlewareAPI));

我们发现store是middlewareAPI,

//store
var middlewareAPI = {
    getState: store.getState,
    dispatch: (action) => dispatch(action)
}

然后就剩下

next => action => {
    let result = next(action); // 返回的也是同样的action值
    console.log(''dispatch'', action);
    console.log(''nextState'', store.getState());
    return result;
};
中间层next
//源码分析
dispatch = compose(...chain)(store.dispatch)

先来分析compose(...chain)

//compose源码
export default function compose(...funcs) {
    if (funcs.length === 0) {
        return arg => arg
    }

    if (funcs.length === 1) {
        return funcs[0]
    }

    const last = funcs[funcs.length - 1]
    const rest = funcs.slice(0, -1)
    return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))
}

compose利用Array.prototype.reduceRight的方法

//reduceRight遍历介绍
[0, 1, 2, 3, 4].reduceRight(function(previousValue, currentValue, index, array) {
    return previousValue + currentValue;
}, 10);

//结果 10+4+3+2+1+0 = 20

因为我们这里的中间件就只有一个,所以没有使用到reduceRight直接返回,直接返回func[0](本身);再由compose(...chain)(store.dispatch),我们可以知道next就是store.dispatch

(action) => {
    let result = store.dispatch(action); // 这里的next就是store.dispatch
    console.log(''dispatch'', action);
    console.log(''nextState'', store.getState());
    return result;
};

我们之后调用的dispath就是触发的是上面这个函数(这里就单个中间件);

多个中间件

  • 通过上面的 applyMiddleware , compose 和中间件的结构,

  • 假设应用了如下的中间件: [A, B, C],这里我们使用es5的结构做分析

  • 分析action触发的完整流程

三个中间件

//A
function A(store) {
    return function A(next) {
        return function A(action) {
            /*...*/;
            next(action);
            /*...*/;
            return /*...*/;
        }
    }
}
//B
function B(store) {
    return function B(next) {
        return function B(action) {
            /*...*/;
            next(action);
            /*...*/;
            return /*...*/;
        }
    }
}
//C
function C(store) {
    return function C(next) {
        return function C(action) {
            /*...*/;
            next(action);
            /*...*/;
            return /*...*/;
        }
    }
}

通过chain = middlewares.map(middleware => middleware(middlewareAPI)),三个中间件的状态变化

//A
function A(next) {
    return function A(action) {
        /*...*/;
        next(action);
        /*...*/;
        return /*...*/;
    }
}
//B
function B(next) {
    return function B(action) {
        /*...*/;
        next(action);
        /*...*/;
        return /*...*/;
    }
}
//C
function C(next) {
    return function C(action) {
        /*...*/;
        next(action);
        /*...*/;
        return /*...*/;
    }
}

再由dispatch = compose(...chain)(store.dispatch),我们转化下

const last = C;
const rest = [A,B]
dispatch = rest.reduceRight(
    (composed, f) =>{
        return f(composed)
    }, 
    last(store.dispatch)
)

我们得到的结果

dispatch = A(B(C(store.dispatch)));

进一步分析,我们得到的结果

dispatch = A(B(C(store.dispatch)));

//执行C(next),得到结果

A(B(function C(action) {/*...*/;next(action);/*...*/;return /*...*/;})); 
//此时的next = store.dispatch

//继续执行B(next)
A(function B(action) {/*...*/;next(action);/*...*/;return /*...*/;});    
//此时的next = function C(action) {/*...*/;next(action);/*...*/;return /*...*/;}

//继续执行A(next)
function A(action) {/*...*/;next(action);/*...*/;return /*...*/;};
//此时的next = function B(action) {/*...*/;next(action);/*...*/;return /*...*/;}

一个action触发执行顺序,A(action) -> B(action) -> C(action) -> store.dispatch(action)(生产最新的 store 数据);

如果next(action)下面还有需要执行的代码,继续执行 C(next 后的代码)->B(next 后的代码)->A(next 后的代码)

总结:先从内到外生成新的func,然后由外向内执行。本来我们可以直接使用store.dispatch(action),但是我们可以通过中间件对action做一些处理或转换,比如异步操作,异步回调后再执行next;这样的设计很巧妙,只有等待next,才可以继续做操作,和平时直接异步回调又有些不一样

项目实践 ->异步

我们知道redux中actions分为actionType,actionCreator,然后在由reducer进行修改数据;

官方例子中async直接在actionCreator做了ajax请求;

我们把ajax放入中间件触发下面要讲的与官方real-world类似

我这边使用redux-thunk

applyMiddleware(reduxThunk, api)

先来看看redux-thunk的源码

function createThunkMiddleware(extraArgument) {
    return ({ dispatch, getState }) => next => action => {
        if (typeof action === ''function'') {//重新分发
            return action(dispatch, getState, extraArgument);
        }
        return next(action);//传递给下一个中间件
    };
}

const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;

export default thunk;

这样一来我们可以把异步写成一个复用的actionCreator;

import * as types from ''../../constants/actions/common'';

export function request(apiName, params, opts = {}) {
    return (dispatch, getState) => {
        let action = {
            ''API'': {
                apiName: apiName,
                params: params,
                opts: opts
            },
            type: types.API_REQUEST
        };
        return dispatch(action);
    };
}


//其他地方调用复用的方法如下:
export { request } from ''./request'';

正常的写法,不是异步的,就是之前的写法

export function cartSelect(id) {
    return { 
        type: types.CART_MAIN_SELECT, 
        id
    };
}

然后就是下一个中间件的处理 api.js

//自己封装的ajax,可以使用别的,比如isomorphic-fetch
import net from ''net'';
//项目中全部的接口,相当于一个关于异步的actionType有一个对应的后端接口
import API_ROOT from ''apiRoot'';

export default store => next => action => {
    let API_OPT = action[''API''];

    if (!API_OPT) {
        //我们约定这个没声明,就不是我们设计的异步action,执行下一个中间件
        return next(action);
    }

    let ACTION_TYPE = action[''type''];
    let { apiName, params = {} , opts = {} } = API_OPT;
    /**
     * 如果有传递localData,就不会触发ajax了,直接触发_success
     * 当前也可以传其他参数
     */
    let { localData } = opts;
    let {
        onSuccess,
        onError,
        onProgress,
        ajaxType = ''GET'',
        param
    } = params;
    // 触发下一个action
    let nextAction = function(type, param, opts) {
        action[''type''] = type;
        action[''opts''] = opts;
        delete param[''onSuccess''];
        delete param[''onError''];
        const nextRequestAction = {...action,...param}
        return nextRequestAction;
    };

    params={
        ...params,
        data: null
    };
    // 触发正在请求的action
    let result = next(nextAction(apiName + ''_ON'', params, opts));
    net.ajax({
        url: API_ROOT[apiName],
        type: ajaxType,
        param,
        localData,
        success: data => {
            onSuccess && onSuccess(data);
            params={
                ...params,
                data
            };
            //触发请求成功的action
            return next(nextAction(apiName + ''_SUCCESS'', params, opts));
        },
        error: data => {
            onError && onError(data);
            //触发请求失败的action
            return next(nextAction(apiName + ''_ERROR'', params, opts));
        }
    });

    return result;
};

强调一点:项目中全部的接口,相当于一个关于异步的actionType有一个对应的后端接口,所以我们才可以通过API_ROOT[apiName]找到这个接口

以cart为列子(下面是对应的每个文件):

actionType:

//异步
export const CART_MAIN_GET = ''CART_MAIN_GET'';
//非异步
export const CART_MAIN_SELECT = ''CART_MAIN_SELECT'';

api:

const api = {
    ''CART_MAIN_GET'':''/shopping-cart/show-shopping-cart''
};
export default api;

APIROOT修改:

import cart from ''./api/cart'';
const APIROOT = {
    ...cart
};
export default API;

actionCreator:

//项目中使用redux的bindActionCreators做一个统一的绑定,所以在这里单独引入
export { request } from ''./request'';
//下面是非异步的方法
export function cartSelect(id) {
    return { 
        type: types.CART_MAIN_SELECT, 
        id
    };
}

项目中发起结构是这样的:

let url = types.CART_MAIN_GET;
let param = {};
let params = {
    param: param,
    ajaxType: ''GET'',
    onSuccess: (res) => {
        /*...*/
    },
    onError: (res) => {
        /*...*/
    }
};
request(url, params, {});

其对应的reducers就是下面

import * as types from ''../constants/actions/cart'';
const initialState = {
    main:{
        isFetching: 0,//是否已经获取 
        didInvalidate:1,//是否失效
        itemArr:[],//自定义模版
        itemObj:{},//自定义模版数据
        header:{}//头部导航
    }
};
export default function(state = initialState, action) {
    let newState;
    switch (action.type) {
        case types.HOME_MAIN_GET + ''_ON''://可以不写
            /*...*/
            return newState;
        case types.HOME_MAIN_GET + ''_SUCCESS'':
            /*...*/
            return newState;
        case types.HOME_MAIN_GET + ''_ERROR''://可以不写
            /*...*/
            return newState;
        default:
            return state;
    }
};

异步,数据验证都可以通过中间件做处理;引用Generator,Async/Await,Promise处理,可以参考社区中的一些其他方式,比如:

  • redux-promise

  • redux-saga

react+react-redux demo 连载 如何自学前端

react+react-redux demo 连载 如何自学前端

import React from "react";

import {browserHistory, IndexRoute,Redirect, Route, Router} from "react-router";

//引入app

import app from"../pages/app.jsx";

//引入home 页面

import pageHome from"../pages/home.jsx";

//webpack会打包成chunk包 在进入album时去加载

const pageAlbum = (location, cb) => {

 require.ensure([], require => {

   cb(null, require(''../pages/album.jsx'').default)

  },''pageAlbum'')

};

...

//定义路由 首先加载 app

//然后根据正则去匹配path来实现路由页面

const RouteConfig = (

 <Router history={browserHistory}>

   <Route path="/" component={app}>

     <IndexRoute component={pageHome}/>

     <Route path="home" component={pageHome}/>

     <Route path="album" getComponent={pageAlbum}/>

     <Route path="feedback" getComponent={pageFeedback}/>

     <Route path="blist" getComponent={pageBlogList}/>

     <Route path="bdetail" getComponent={pageBlogDetail}/>

     <Redirect from=''*'' to=''/''/>

   </Route>

 </Router>

);

 

export default RouteConfig;

技术问题,可以+618237474一起讨论交流

react+react-router+redux+Node.js+socket.io写一个聊天webapp

react+react-router+redux+Node.js+socket.io写一个聊天webapp

一、项目预览

之前看一个写聊天器的教程,自己也跟着教程做了一遍,由于懒得去找图片和一些图标我就用教程中的素材来做,主要是用了react+react-router+redux+Node.js+socket.io的技术栈,接下来就是项目的预览

1.首先在/login下能看到有登录和注册按钮

登录注册页

2.点击注册按钮,路由跳到/register,注册一个账号,用户和密码都为LHH,选择“牛人”,点击注册,之后路由会跳到/geniusinfo,即牛人完善信息页,选择一个头像并完善信息后点击保存按钮

注册页
完善信息页

3.可以看到已经进入有三个tab选项的内容页面了,点击“我”,路由跳转到/me即可看到个人中心内容,但此时boss和消息的tab页仍没有内容,可以按照之前步骤注册一个Boss账号,只需在注册的时候选择Boss选项

个人中心

4.现在在LHH和LCE账号分别能看到的列表

列表

5.点击进入聊天室,输入内容

聊天室

二、接下来对项目的主要内容进行解释

1.项目的除掉node_modules后的目录
├─build
│  └─static
│      ├─css
│      └─js
├─config
│  └─jest
├─public
├─scripts
├─server
└─src
    ├─component
    │  ├─authroute
    │  ├─avatar-selector
    │  ├─boss
    │  ├─chat
    │  ├─dashboard
    │  ├─genius
    │  ├─img
    │  ├─logo
    │  ├─msg
    │  ├─navlink
    │  │  └─img
    │  ├─user
    │  └─usercard
    ├─container
    │  ├─bossinfo
    │  ├─geniusinfo
    │  ├─login
    │  └─register
    └─redux

其中build文件夹的内容为npm run build打包后的内容,在项目中如果启用后端接口也可访问

2.入口页面
import React from ''react'';
import ReactDOM from ''react-dom'';
import { createStore, applyMiddleware, compose } from ''redux'';
import thunk from ''redux-thunk'';
import { Provider } from ''react-redux'';
// eslint-disable-next-line
import { BrowserRouter } from ''react-router-dom'';
import App from ''./app''

import reducers from ''./reducer''
import ''./config''
import ''./index.css''

const store = createStore(reducers, compose(
    applyMiddleware(thunk),
    window.devToolsExtension?window.devToolsExtension():f=>f
))

// boss genius me msg 4个页面
ReactDOM.render(
    (<Provider store={store}>
        <BrowserRouter>
            <App></App>
        </BrowserRouter>
    </Provider> ),
    document.getElementById(''root'')
 )

使用react-redux的Provider,可实现全局的状态存储,子组件可通过props获得存储在全局的状态

const store = createStore(reducers, compose(
    applyMiddleware(thunk),
    window.devToolsExtension?window.devToolsExtension():f=>f
))

上面代码的主要作用是关于配置浏览器的redux插件的,可以通过这个插件在控制台中查看state中的数据。
来看下app.js中的代码

import React from ''react''
import Login from ''./container/login/login.js'';
import Register from ''./container/register/register.js'';
import AuthRoute from ''./component/authroute/authroute.js'';
import BossInfo from ''./container/bossinfo/bossinfo.js'';
import Geniusinfo from ''./container/geniusinfo/geniusinfo'';
import Dashboard from ''./component/dashboard/dashboard'';
import Chat from ''./component/chat/chat''
import {  Route,  Switch } from ''react-router-dom'';

class App extends React.Component{
    render() {
        return (
            <div>
                <AuthRoute></AuthRoute>
                <Switch>
                    <Route path=''/bossinfo'' component={BossInfo}></Route>
                    <Route path=''/geniusinfo'' component={Geniusinfo}></Route>
                    <Route path=''/login'' component={Login}></Route>
                    <Route path=''/register'' component={Register}></Route>
                    <Route path=''/chat/:user'' component={Chat}></Route>
                    <Route component={Dashboard}></Route>
                </Switch>
                
            </div>
        )
    }
}
export default App

这里主要是讲主页面中的代码分割出来。
authroute.js中是路由跳转的逻辑判断
页面中的UI组件也用到了antd-mobile插件
客户端接收和传送数据得引入socket.io-client,代码在chat.redux.js中。
聊天器中需要存储在数据库的内容主要为from(发送端)、to(接收端)、read(是否已读)、content(聊天内容)、create_time(聊天时间)而且还需要一个唯一的chatid来代表这个聊天室的唯一性,可以用fromto拼接,拼接函数写在util.js中。

3.Server

后端接口用到了node.jsexpress框架,数据库用到了mongodb,在server文件夹中存放连接数据库的文件,model.js在直接与mongodb数据库连接,

const mongoose = require(''mongoose'');
// 连接mongo,并且使用my_app这个集合
const DB_URL = "mongodb://localhost:27017/chat_app";
mongoose.connect(DB_URL);

const models = {
    user: {
        ''user'': { ''type'': String, ''require'': true },
        ''pwd'': { ''type'': String, ''require'': true },
        ''type'': { ''type'': String, ''require'': true },
        // 头像
        ''avatar'': { ''type'': String },
        // 个人简介或者职位简介
        ''desc'': { ''type'': String },
        // 职位名
        ''title'': { ''type'': String },
        // 如果是boss,还有两个字段
        ''company'': { ''type'': String },
        ''money'': { ''type'': String }
    },
    chat: {
        ''chatid'': { ''type'': String, ''require'': true },
        ''from'': { ''type'': String, ''rewuire'': true },
        ''to'': { ''type'': String, ''require'': true },
        ''read'': { ''type'': String, ''require'': true },
        ''content'': { ''type'': String, ''require'': true, ''default'': '''' },
        ''create_time'': { ''type'': Number, ''default'': new Date().getTime() }
    }
}
for (let m in models) {
    mongoose.model(m, new mongoose.Schema(models[m]))
}
module.exports = {
    getModel: function(name) {
        return mongoose.model(name)
    }
}

连接的数据库端口号为27017,这个视自己电脑的数据库端口号而定。
server.js中引入了http、express、socket.io插件,服务端用的是9093端口,

const express = require(''express'');
const bodyParser = require(''body-parser'');
const cookieParser = require(''cookie-parser'');
const model = require(''./model'')
    // const User = model.getModel(''user'');
const Chat = model.getModel(''chat'');
const path = require(''path'')
const app = express();
//work with express
const server = require(''http'').Server(app);
const io = require(''socket.io'')(server);
io.on(''connection'', function(socket) {
    // console.log(''user login'')
    socket.on(''sendmsg'', function(data) {
        const { from, to, msg } = data;
        const chatid = [from, to].sort().join(''_'');
        Chat.create({ chatid, from, to, content: msg }, function(err, doc) {
                // console.log(doc._doc)
                io.emit(''recvmsg'', Object.assign({}, doc._doc))
            })
            // console.log(data);
            // io.emit(''recvmsg'', data)
    })
})
const userRouter = require(''./user'');
app.use(cookieParser());
app.use(bodyParser.json())
app.use(''/user'', userRouter);
app.use(function(req, res, next) {
    if (req.url.startsWith(''/user/'') || req.url.startsWith(''/static/'')) {
        return next()
    }
    return res.sendFile(path.resolve(''build/index.html''))
})
app.use(''/'', express.static(path.resolve(''build'')))
server.listen(9093, function() {
    console.log(''Node app start at port 9093'')
});

客户端用到的接口写在user.js

const express = require(''express'')
const Router = express.Router();
const model = require(''./model'')
const User = model.getModel(''user'');
const Chat = model.getModel(''chat'');
const _filter = { ''pwd'': 0, ''__v'': 0 };

// 删除所有聊天记录
// Chat.remove({}, function(e, d) {})
// 加密
const utils = require(''utility'');
Router.get(''/list'', function(req, res) {
    const { type } = req.query
    // 删除所有用户
    // User.remove({}, function(e, d) {})
    User.find({ type }, _filter, function(err, doc) {
        return res.json({ code: 0, data: doc })
    })
});
Router.get(''/getmsglist'', function(req, res) {
    const user = req.cookies.userid;
    User.find({}, function(err, userdoc) {
        let users = {};
        userdoc.forEach(v => {
            users[v._id] = { name: v.user, avatar: v.avatar }
        })
        Chat.find({ ''$or'': [{ from: user }, { to: user }] }, function(err, doc) {
            // console.log(doc)
            if (!err) {
                return res.json({ code: 0, msgs: doc, users: users })
            }
        })
    })
})
Router.post(''/readmsg'', function(req, res) {
    const userid = req.cookies.userid;
    const { from } = req.body;
    // console.log(userid, from)
    Chat.update({ from, to: userid }, { ''$set'': { read: true } }, { ''multi'': true },

        function(err, doc) {
            if (!err) {
                return res.json({ code: 0, num: doc.nModified })
            }
            return res.json({ code: 1, msg: ''修改失败'' })
        })
})
Router.post(''/update'', function(req, res) {
    const userid = req.cookies.userid;
    if (!userid) {
        return json.dumps({ code: 1 });
    }
    const body = req.body;
    User.findByIdAndUpdate(userid, body, function(err, doc) {
        const data = Object.assign({}, {
            user: doc.user,
            type: doc.type
        }, body)
        return res.json({ code: 0, data })
    })
});
Router.post(''/login'', function(req, res) {
    const { user, pwd } = req.body;
    User.findOne({ user, pwd: md5Pwd(pwd) }, _filter, function(err, doc) {
        if (!doc) {
            return res.json({ code: 1, msg: ''用户名或者密码错误'' });
        }
        res.cookie(''userid'', doc._id)
        return res.json({ code: 0, data: doc })
    })
});
Router.post(''/register'', function(req, res) {
    console.log(req.body);
    const { user, pwd, type } = req.body;
    User.findOne({ user }, function(err, doc) {
        if (doc) {
            return res.json({ code: 1, msg: ''用户名重置'' })
        }
        const userModel = new User({ user, pwd: md5Pwd(pwd), type });
        userModel.save(function(e, d) {
            if (e) {
                return res.json({ code: 1, msg: ''后端出错了'' })
            }
            const { user, type, _id } = d;
            res.cookie(''userid'', _id)
            return res.json({ code: 0, data: { user, type, _id } })
        })
    })
})
Router.get(''/info'', function(req, res) {
    const { userid } = req.cookies;
    if (!userid) {
        return res.json({ code: 1 })
    }
    User.findOne({ _id: userid }, _filter, function(err, doc) {
            if (err) {
                return res.json({ code: 1, msg: ''后端出错了'' })
            }
            if (doc) {
                return res.json({ code: 0, data: doc })
            }
        })
        // 用户有没有cookie

});
// 密码加盐
function md5Pwd(pwd) {
    const salt = ''lhh_is_good_1310486!@#5^%~*'';
    return utils.md5(utils.md5(pwd + salt))
}
module.exports = Router

三、总结

本项目实现了获取数据和表现的代码分离,也是对于学习React、Node和WebSocket的一次更进一步提升,当然还有很多可以改进的地方,比如可以用asyncawait进行异步获取数据等等。
作为一名前端菜鸟,还是希望前辈能给一些学习的建议和指点迷津
最后附上本项目的代码链接
github链接

我们今天的关于React Redux 中间件思想遇见 Web Worker 的灵感附demo的分享已经告一段落,感谢您的关注,如果您想了解更多关于React 从入门到放弃 (4):Redux 中间件、React 项目中Redux 中间件的理解、react+react-redux demo 连载 如何自学前端、react+react-router+redux+Node.js+socket.io写一个聊天webapp的相关信息,请在本站查询。

本文将带您了解关于react-native报 Execution failed for task ':app:mergeDebugResources'错误的新内容,同时我们还将为您解释react-native-debugger的相关知识,另外,我们还将为您提供关于Android Error:Execution failed for task '':app:compileDebugJavaWithJavac'' 解决方案 总结、Android Error:Execution failed for task '':app:preDebugAndroidTestBuild''. > Conflict wi...、Android Studio Error:Execution failed for task '':app:preDebugAndroidTestBuild''. 彻底解决的方法以及修...、android studio 中Error:Execution failed for task '':app:preDebugAndroidTestBuild''. 的解决办法的实用信息。

本文目录一览:

react-native报 Execution failed for task ':app:mergeDebugResources'错误(react-native-debugger)

react-native报 Execution failed for task ':app:mergeDebugResources'错误(react-native-debugger)

控制台报错信息:


解决方式:

在app文件夹下的build.gradle中配置下面两个信息,再clear project

aaptOptions.cruncherEnabled = false
aaptOptions.useNewCruncher = false

如图所示:

Android Error:Execution failed for task '':app:compileDebugJavaWithJavac'' 解决方案 总结

Android Error:Execution failed for task '':app:compileDebugJavaWithJavac'' 解决方案 总结

1、升级更新buildToolsVersion 到最新版本
2、升级Android Studio 2.2.2版本,JDK 1.8版本
3、配置JAVA_HOME路径

然并卵没有解决我个人的问题

 

gradlew compileDebug --stacktrace 可以用于查看堆栈的错误信息,然后再去网上继续找寻错误信息。

把命令gradlew compileDebug --stacktrace 改成 gradlew compileDebugJavaWithJavac

居然把具体的错误信息打印出来了

然后更改,解决问题

像我遇到的问题是,刷新的时候都没有问题,运行的时候就出现我们标题的问题,然而解决了几次这样错误以后,发现一般来说只有manefest的权限重复这种类型错误,不打印错误信息出来,是看不到真正的错误的。gradle和studio升级版本越来越高以后,对权限要求都不能重复了,感觉算不算坑,有点追求完美?以前版本是可以兼容,内部应该帮你合并了的。

 

环境:studio3.0.1  gradle 4.1版本

Android Error:Execution failed for task '':app:preDebugAndroidTestBuild''. > Conflict wi...

Android Error:Execution failed for task '':app:preDebugAndroidTestBuild''. > Conflict wi...

错误内容:

Error:Execution failed for task '':app:preDebugAndroidTestBuild''.
> Conflict with dependency ''com.android.support:support-annotations'' in project '':app''. Resolved versions for app (26.1.0) and test app (27.1.1) differ. See https://d.android.com/r/tools/test-apk-dependency-conflicts.html for details.

 

我们寻找External Libraries中的27.1.1版本,删掉就行了

最后在build.gradle中添加如下代码

androidTestCompile(''com.android.support:support-annotations:26.1.0'') {
        force = true
    }

 

Android Studio Error:Execution failed for task '':app:preDebugAndroidTestBuild''. 彻底解决的方法以及修...

Android Studio Error:Execution failed for task '':app:preDebugAndroidTestBuild''. 彻底解决的方法以及修...

Error

Error:Execution failed for task '':app:preDebugAndroidTestBuild''. > 
Conflict with dependency ''com.android.support:support-annotations'' in project '':app''. 
Resolved versions for app (26.1.0) and test app (27.1.1) differ. 
See https://d.android.com/r/tools/test-apk-dependency-conflicts.html for details.

问题说明

因为使用的依赖包版本不同的原因,所以,我们强制使用一样的版本即可解决问题

在 adroid 结点下添加下述代码

configurations.all {
    resolutionStrategy.force ''com.android.support:support-annotations:26.1.0''
}

把版本号修改一下即可

一劳永逸的办法

上面的办法在当前的项目是已经解决了的,但是,新建一个项目又会出现同样的问题,这就很烦了。

我们直接通过修改新建一个项目的模板,直接把默认的那些设置改了,即可达成一劳永逸

我的版本是 Android Studio 3.0.1 网上查找修改这些默认设置的时候,资料发现不太一样,自己摸索也是找到了关键的地方

找到路径 Android Studio的根目录\plugins\android\lib\templates\gradle-projects\NewAndroidModule\rootshared_macros.ftl 文件,上面自己需要的代码复制在 android 结点下即可

扩展,修改 buildToolVersion targetVersion gradleVersion 等默认版本

  • appcompat 版本号 -- Android Studio的根目录\plugins\android\lib\templates\gradle-projects\NewAndroidModulerecipe.xml.ftl

    <#if backwardsCompatibility!true> <dependency mavenUrl="com.android.support:appcompat-v7:25.3.1" /> </#if>

  • compileSdkVersion,buildToolsVersion,targetSdkVersion 等版本号 -- Android Studio的根目录\plugins\android\lib\templates\gradle-projects\NewAndroidModule\rootshared_macros.ftl

      android {
          compileSdkVersion 25
          <#if compareVersions(gradlePluginVersion, ''3.0.0'') lt 0>buildToolsVersion 27.0.1</#if>
    
          <#if isBaseFeature>
          baseFeature true
          </#if>
    
          defaultConfig {
          <#if hasApplicationId>
              applicationId "${applicationId}"
          </#if>
              minSdkVersion <#if minApi?matches("^\\d+$")>${minApi}<#else>''${minApi}''</#if>
              targetSdkVersion 25
              versionCode 1
              versionName "1.0"
    

小工具(懒人必备)

AlterASDefaultSetting

android studio 中Error:Execution failed for task '':app:preDebugAndroidTestBuild''. 的解决办法

android studio 中Error:Execution failed for task '':app:preDebugAndroidTestBuild''. 的解决办法

  使用AndroidStudio工具创建新的app应用时,根据工具提示进行多个next之后,发现编译报错,报错如下:

  

Error:Execution failed for task '':app:preDebugAndroidTestBuild''.
> Conflict with dependency ''com.android.support:support-annotations'' in project '':app''. Resolved versions for app (26.1.0) and test app (27.1.1) differ. See https://d.android.com/r/tools/test-apk-dependency-conflicts.html for details.

 

  经过网上查阅,发现提供有很多方法,比如 build->Rebuid-project,该方法只是当前启动有用,但是如果关闭androidstudio工具重新打开之后又会重新报错。

  该问题主要是因为在新建的应用中默认依赖了com.android.support:support-annotations包与app版本冲突了,app里的版本是26.1.0,但是Test app的版本里是27.1.1。可在External Libraries文件夹下面找到该包,发现确实冲突。

  最后经过查阅,发现两种方法可以解决该问题,思路都是一样的,就是将27.1.1版本号强制转换为26.1.0,转换方法如下:

 (1)在依赖中强制转换,在app模块下的build.gradle中dependencies下,添加依赖转换,主要是下面两行代码

    

androidTestCompile(''com.android.support:support-annotations:26.1.0'') {
        force = true
    }

 

   全部代码为:

    

apply plugin: ''com.android.application''

apply plugin: ''kotlin-android''

apply plugin: ''kotlin-android-extensions''

android {
    compileSdkVersion 26
    defaultConfig {
        applicationId "baseapp.li.com.baseapp"
        minSdkVersion 15
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile(''proguard-android.txt''), ''proguard-rules.pro''
        }
    }
}

dependencies {
    implementation fileTree(dir: ''libs'', include: [''*.jar''])
    implementation"org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
    implementation ''com.android.support:appcompat-v7:26.1.0''
    implementation ''com.android.support.constraint:constraint-layout:1.1.3''
    testImplementation ''junit:junit:4.12''
    androidTestImplementation ''com.android.support.test:runner:1.0.2''
    androidTestImplementation ''com.android.support.test.espresso:espresso-core:3.0.2''
    androidTestImplementation(''com.android.support:support-annotations:26.1.0'') {
        force = true
    }
}

 

 (2)在配置中强制转换,在app模块下的build.gralde中进行配置,其中主要增加了两行代码

      

configurations.all {
    resolutionStrategy.force ''com.android.support:support-annotations:26.1.0''
}

 

     全部代码如下:

apply plugin: ''com.android.application''
 
configurations.all {
    resolutionStrategy.force ''com.android.support:support-annotations:26.1.0''
}
 
android {
    compileSdkVersion 26
    defaultConfig {
        applicationId "wzt.mytest"
        minSdkVersion 21
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile(''proguard-android.txt''), ''proguard-rules.pro''
        }
    }
}
 
dependencies {
    implementation fileTree(dir: ''libs'', include: [''*.jar''])
    implementation ''com.android.support:appcompat-v7:26.1.0''
    testImplementation ''junit:junit:4.12''
    androidTestImplementation ''com.android.support.test:runner:1.0.2''
    androidTestImplementation ''com.android.support.test.espresso:espresso-core:3.0.2''
}

 

 

以上两种方法均可解决包名冲突问题。下面这个代码code背景不知道怎么去掉了,容许我调皮一下,应该无大碍。

 

 

原文出处:https://www.cnblogs.com/fei-android/p/10364265.html

我们今天的关于react-native报 Execution failed for task ':app:mergeDebugResources'错误react-native-debugger的分享就到这里,谢谢您的阅读,如果想了解更多关于Android Error:Execution failed for task '':app:compileDebugJavaWithJavac'' 解决方案 总结、Android Error:Execution failed for task '':app:preDebugAndroidTestBuild''. > Conflict wi...、Android Studio Error:Execution failed for task '':app:preDebugAndroidTestBuild''. 彻底解决的方法以及修...、android studio 中Error:Execution failed for task '':app:preDebugAndroidTestBuild''. 的解决办法的相关信息,可以在本站进行搜索。

在本文中,我们将为您详细介绍Webpack 配置详解及实现过程的相关知识,并且为您解答关于webpack简单配置的疑问,此外,我们还会提供一些关于02-webpack的基本配置和输入webpack实现自动打包、Gulp & webpack 配置详解、React+TypeScript+webpack4多入口配置详解、The way of Webpack learning (I.) -- Configure Webpack from zero(从零开始配置webpack)的有用信息。

本文目录一览:

Webpack 配置详解及实现过程(webpack简单配置)

Webpack 配置详解及实现过程(webpack简单配置)

Webpack 配置详解

一、打包升级

1.基础打包配置

1. 开发模式(devtool)

demo_base版本

This option controls if and how source maps are generated.

开发建议使用eval模式,缺点是无法正确显示行号,想要正确显示行号,可以时候用source-map或者eval-source-map

生产环境: 建议使用cheap-module-source-map

2. 入口配置(entry)

string | [string] | object { <key>: string | [string] } | (function: () => string | [string] | object { <key>: string | [string] })

入口打包根场景不同,入口配置也不同。

  • 单入口:

entry: './A/index.js'

entry: [
'./A/index.js',],
  • 多入口:

entry: [
'./A/index.js','./B/index.js'
],entry: {
A: './A/index.js',B: './B/index.js'
}

3. 输出配置(output)

  • 输出路径配置:

    output: {
        // path.resolve用来拼接文件多级目录
        // __dirname 为当前文件所在全路径地址
        path: path.resolve(__dirname,'dist'),// 输出文件名字
       // filename: 'app.js',// 以key作为文件名输出
        filename: '[name].js',// chunkhash 根据文件内容生成特点的hash,使用这个可以保证文件内容不变,那么文件名字就不会改变,可以用来作为热更新
        chunkFilename: '[chunkhash].js'
    }

4. resolve

Configure how modules are resolved. For example,when calling import "lodash" in ES2015,the resolve options can change where webpack goes to look for "lodash" (see modules).

resolve: {
        // 当你reuire时,不需要加上以下扩展名
        extensions: ['.js','.md','.txt'],},

5. 插件(plugin)

plugins: [
    // Webpack 2以后内置
    // new webpack.optimize.OccurrenceOrderPlugin(),// 碰到错误warning但是不停止编译
    new webpack.NoEmitOnErrorsPlugin(),// 开发模式不需要压缩
    // new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }),

6. moudles

* babel-loader(用来做js代码转化)
* style-loader & css-loader(用来转化css代码)
* less-loader 转化less文件
* raw-loader 把文件当做普通的文本文件读取
* json-loader webpack 2以后就不需要配置了(内置了)
* url-loader 用来处理eot|woff|woff2|ttf|svg|png|jpg这些文件,可以防止加载资源文件导致页面加载缓慢
* file-loader 用来处理文件,可以用url-loader代替,但是如果你资源文件是即时文件,那么就使用file-loader

2.多入口打多个包 (生成多个bundle.js)

这个是webpack 3.1.0新出来的配置方式,可以用来解决多个入口文件,打包成多个文件夹的问题。

demo 将多个入口打成多个文件夹

module.exports = [{
  output: {
    filename: './dist-amd.js',libraryTarget: 'amd'
  },entry: './app.js',{
  output: {
    filename: './dist-commonjs.js',libraryTarget: 'commonjs'
  },}]

3.兼容多浏览器,添加postcss-loader(生产环境使用,增加build和rebuild时间)

demo 添加postcss

添加postcss-loader,需要做如下配置

webpack config 配置

插件配置

{
        test: /\.less/,use: [
          'style-loader','css-loader',+          'postcss-loader','less-loader'
        ]
      },{
        test: /\.css$/,-        use: 'style-loader!css-loader',+        use: 'style-loader!css-loader!postcss-loader',
.postcss.config.js文件配置
module.exports = {
  plugins: {
    'postcss-import': {},// 能够使用import语法 @import "cssrecipes-defaults"; 
    'postcss-cssnext': {},//Postcss-cssnext是一个PostCSS插件,可以帮助您使用最新的CSS语法。 它将CSS规范转换为更兼容的CSS,因此您不需要等待浏览器支持。
    'cssnano': {}
  }
}

4.css文件抽离 (生成环境使用,会增加build和rebuild时间)

demo css文件分离

webpack config 配置

插件配置

+const ExtractTextPlugin = require('extract-text-webpack-plugin');

+new ExtractTextPlugin('style.css'),//名字配置
    { 
        test: /\.less/,_        use: [
_          'style-loader',_          'css-loader',_          'less-loader'
_        ]
+        use: ExtractTextPlugin.extract({
+          fallback: 'style-loader',+          use: ['css-loader','less-loader']
+        })
      },+         use: ExtractTextPlugin.extract({
+          fallback: 'style-loader',+          use: ['css-loader']
+        })
      },//Postcss-cssnext是一个PostCSS插件,可以帮助您使用最新的CSS语法。 它将CSS规范转换为更兼容的CSS,因此您不需要等待浏览器支持。
    'cssnano': {}
  }
}

5.公共文件抽取 (抽取公共文件,可以减少build与rebuild时间)

公共文件抽取一般依靠 CommonChunkPlguin 和 DllPlugin这两个插件.

CommonChunkPlugin Demo

DllPlugin Demo

  • 共同点:

    • 都可以抽出公共模块

  • 不同点:

    • CommonChunkPlguin

      1. CommonChunkPlguin可以抽出多个模块间公共模块

      2. 配置了HtmlWebpackPlugin后,不需要手动在html中导入

    • DllPlugin

      1. DllPlugin 可以在multi compliler(多个webpack config 文件) 中使用

      2. DllPlugin 生成的文件相当于独立的存在,就像jQuery一样,需要你在html进行引入之后才能使用。

CommonChunkPlugin 配置:

// 如果有其他CommonsChunkPlugin生成的文件,将会引入
  // - If chunk has the name as specified in the chunkNames it is put in the list
  // - If no chunk with the name as given in chunkNames exists a new chunk is created and added to the list
 // 大概意思就是如果name在entry里面有,那就加入一个列表,如果entry里面没有,
 // 那么就创建一个新chunk列表,如果chunks里面相同模块代码出现次数超过minChunks,那就添加到这个新创建的list里面。
 new webpack.optimize.CommonsChunkPlugin({
      name: "common",chunks: ["a","b"],//需要合并的文件
      // minChunks:3 //最少在出现过多少次才将其打入common中
    }),//如果
    new webpack.optimize.CommonsChunkPlugin({
      name: "vendor",minChunks: Infinity 
    })

DllPlugin 配置:

添加文件
const webpackConfig = {
  name: "vendor",entry: ["react","react-dom"],output: {
    path: buildpath,// 输出文件路径
    filename: "vendor.js",library: "vendor_[hash]"
  },plugins: [
    new webpack.DllPlugin({
      name: "vendor_[hash]",path: path.resolve(buildpath,"manifest.json")
    })
  ]
};
name: "app",+ dependencies: ["vendor"],devtool: 'eval',+  new webpack.DllReferencePlugin({
+     manifest: path.resolve(buildpath,"manifest.json")
+    }),

6.文件分析(visualizer)

文件分析可以插件可以帮助查看我们生成的bundle.js和chunk的组成成分,可以根据这个进行代码优化。(开发环境使用)

+    const StatsWriterPlugin = require('webpack-stats-plugin').StatsWriterPlugin;
+    const Visualizer = require('webpack-visualizer-plugin');

+   new StatsWriterPlugin({
+      fields: null,+      stats: { chunkModules: true }
+    }),+    new Visualizer({
+      filename: './statistics.html' // visualizer 文件名称,在output 设置的path文件夹可以找到
+    })

7.DefinePlugin(生产环境配置可以减少文件体积)

这个减少文件体积是相对的,webpack打包的时候回删去无用的代码,而react-dom等一些文件中都有很多下面的代码形式,这样webpack 和 DefinePlugin插件配合可以减少部分文件体积

if (process.env.NODE_ENV !== 'production') {}
new webpack.DefinePlugin({
  'process.env': {
    NODE_ENV: JSON.stringify('production')
  }
}),

8.OccurrenceOrderPlugin内置加入,不需要配置

9.UglifyJsPlugin(压缩文件,减小文件体积,生产环境使用)

webpack 本身内置uglifyjs,如果你想控制uglifyjs的版本,可以使用这个。

const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
 new uglifyJsPlugin({
      compress: {
        warnings: false
      }
    })

10.热替换配置(开发环境自动刷新)

篇幅过大,移至此文章

02-webpack的基本配置和输入webpack实现自动打包

02-webpack的基本配置和输入webpack实现自动打包

1安装webPack的方式
    01==》    第一次全局安装  npm i webpack -g  第一次安装了之后以后就不需要在安装了
查看webpack的版本,webpack -V 我的版本是3.8.1
如果出现对应的版本, 你就不需要再次执行
npm i webpack -g
说明你已经安装过webpack了;
       02==》 创建一个文件夹;如testwebpack
        在项目根录下(testwebpack)运行 npm i webpack --save-dev 安装到项目依赖中去 

会出现两个文件夹;node_modules package.json这个两个文件夹
        如果没有 package.json ;你就执行   npm init -y 

 

2尝试使用webpack

         安装包描述文件  npm init -y  新版本的node会自带这个的;
npm i jquery
-S 安装jq 保存到依赖列表中去了;
此时你就可以在package.json 看见自己已经安装了jquery了

           testwebpack目录下==》新建src目录 在src目录下 新建index.html文件 在src目录下新建main.js文件
         src目录下创建 imgs文件夹 和js文件夹(文件结构不要错了)

       

         在main.js文件中导入jq   导入的语法是  import **  from    **它是es6中导入模块的方式 
            1 如 import $ from  'jquery'   注意大小写
             从引入文件可以知道 首先是在自己的兄弟目录中去找  
然后去父级目录的兄弟目录中去找 然后依次类推(重点)
               import $ from "jquery";
             $(function(){
                console.log("haha")
             })
             在node.js中 你要是这样写的 const $=require("Jquery")
         

             由于es6的import导包的方式台高级了 所以浏览器解析不了 所以import $ from 'Jquery'汇报错
             所以需要webpack来惊醒转化
运行webpack。
webpack ./src/mian.js  ./dist/testindex.js 在dist中被编译的文件是为testindex.js

此时你就会发现,项目目录下多了一个dist文件夹;和文件夹下有个文件;

注意:如果如果你配置了webpack.config.js,再次运行上面的命令;会报错

  

配置webpack和命令运行;

  3在项目的跟目录下创建webpack.config.js文件配置如下;

    const path = require("path");
   module.exports={
        entry:path.join(__dirname,'./src/main.js'),//入口文件 使用webpack要打包哪一个文件
            output:{ 输出相关的配置
                path:path.join(__dirname,'./dist'),1)">指定打包好的文件会输出到哪一个目录(dist)下去
                filename:"testindex.js" //指定打包好的文件的名称叫什么名字
             }
   }


  3所以在index中的时候我们就引入被webpack编译好的js文件就可以了
  <script src="../dist/testindex.js"></script>
这里引入的时候需要注意的点是:
之所以是testindex.js是因为配置写的编译成testindex.js

    如果你已经向上面这样配置了  可以直接在项目的根目录下webpack就可以运行了 

    因为已经配置好了

 

webpack 能够处理的问题
ebpack能够处理js文件之间的互相依赖关系
webpack能够处理js的兼容性问题   能装将es6的语法转化为es5的语法  

 

我们可以发现引入jquery的两种方式

const $=require("Jquery")

import $ from  'jquery'

 

Gulp & webpack 配置详解

Gulp & webpack 配置详解

1. Gulp VS webpack 比较

Gulp 是一个任务管理工具,让简单的任务更清晰,让复杂的任务易于掌控;而 webpack 的理念是,一切皆为模块,每个模块在打包的时候都会经过一个叫做 loader 的东西,它具备非常强大的精细化管理能力,主要解决的是依赖分析问题。

Gulp:搞清楚 gulp.src, gulp.dest, gulp.task, gulp.watch 四个 API 就差不多了,它的底层原理是使用 Node 的 Transform Streams,这是一个可读可写可做中间转换的 Streams 管道,由于从 src 到 dest 过程中,文件一直停留在 Streams 中,没有落地成为实体文件,所以整体运作效率非常高。

gulp 常用插件:

  • gulp-load-plugins:自动加载 package.json 中的 gulp 插件
  • gulp-rename: 重命名
  • gulp-uglify:文件压缩
  • gulp-concat:文件合并
  • gulp-less:编译 less
  • gulp-sass:编译 sass
  • gulp-clean-css:压缩 CSS 文件
  • gulp-htmlmin:压缩 HTML 文件
  • gulp-babel:使用 babel 编译 JS 文件
  • gulp-jshint:jshint 检查
  • gulp-imagemin:压缩 jpg、png、gif 等图片
  • gulp-livereload:当代码变化时,它可以帮我们自动刷新页面

Webpack 概念很多,但搞清楚 entryoutputloader 三个关键点,基本上就可以解决简单的问题了,稍微复杂的场景主要包括对资源的合并处理、分拆处理、多次打包等,部分这样的问题可以使用插件辅助解决,但是 Webpack 的强大并不在文件处理,而是依赖分析,所以在流程操作特别复杂的情况,webpack 并不能胜任工作,往往会被作为 gulp 的一个 task,整体工作流交给 gulp 主导。

webpack 常用的 loaderplugin

Loader 列表

  • less-loader, sass-loader:处理样式
  • url-loader, file-loader:两个都必须用上。否则超过大小限制的图片无法生成到目标文件夹中
  • babel-loaderbabel-preset-es2015babel-preset-react:js 处理,转码
  • expose-loader: 将 js 模块暴露到全局

Plugin 列表

  • NormalModuleReplacementPlugin:匹配 resourceRegExp,替换为 newResource
  • ContextReplacementPlugin:替换上下文的插件
  • IgnorePlugin:不打包匹配文件
  • PrefetchPlugin:预加载的插件,提高性能
  • ResolverPlugin:替换上下文的插件
  • DedupePlugin:删除重复或者相似的文件
  • LimitChunkCountPlugin:限制打包文件的个数
  • UglifyJsPlugin:JS文件的压缩
  • CommonsChunkPlugin:共用模块提取
  • HotModuleReplacementPlugin:runtime时候的模块热替换
  • NoErrorsPlugin:跳过编译时出错的代码并记录,使编译后运行时的包不会发生错误。
  • HtmlWebpackPlugin:HTML模块的热更新

--

2. Gulp 简介

Gulp.js 是一个自动化构建工具,开发者可以使用它在项目开发过程中自动执行常见任务。Gulp.js 是基于 Node.js 构建的,利用 Node.js 流的威力,你可以快速构建项目并减少频繁的 IO 操作。Gulp.js 源文件和你用来定义任务的 Gulp 文件都是通过 JavaScript(或者 CoffeeScript )源码来实现的。

image.png

--

2.1 安装 Gulp

1 . 全局安装 gulp

npm install --global gulp

2 . 作为项目的开发依赖(devDependencies)安装:

npm install --save-dev gulp

我们全局安装了gulp,项目也安装了gulp,全局安装gulp是为了执行gulp任务,本地安装gulp则是为了调用gulp插件的功能。

2.2 配置Gulp

在项目根目录下创建一个名为 gulpfile.js 的文件,gulpfile.js是gulp项目的配置文件

var gulp = require(''gulp'');

gulp.task(''default'', function() {
  // 将你的默认的任务代码放在这
});

2.3 运行gulp

在命令提示符执行 gulp 任务名称

<!-- 调用默认任务default -->
gulp  或者  gulp default

2.4 清除文件

通过gulp删除某个文件夹的文件

1 . 安装 gulp-clean

npm i gulp-clean --save-dev

2 . 编写 gulpfile.js 代码

var clean = require(''gulp-clean'');

gulp.task(''clean'', function() {
    return gulp.src([''dist/css'', ''dist/js''], { read: false })
               .pipe(clean());
});

2.5 编译less

通过gulp编译LESS代码

1 . 安装 gulp-less

npm i gulp-less --save-dev

2 . 编写 gulpfile.js 代码

var less = require(''gulp-less'');

gulp.task(''styles'', function() {
    return gulp.src(''src/less/*.less'') //源文件路径
        .pipe(less()) //less编译
        .pipe(gulp.dest(''dist/css'')) //目的路径
});

2.6 自动前缀

通过gulp处理css的自动前缀

1 . 安装 gulp-autoprefixer

npm i gulp-autoprefixer --save-dev

2 . 编写 gulpfile.js 代码

var autoprefixer = require(''gulp-autoprefixer'');

gulp.task(''styles'', function() {
    return gulp.src(''src/css/*.css'') //源文件路径
        .pipe(autoprefixer()) //自动前缀
        .pipe(gulp.dest(''dist/css'')) //目的路径
});

2.7 base64编码

通过gulp将css中的图片转换成base65编码

1 . 安装 gulp-base64

npm i gulp-base64 --save-dev

2 . 编写 gulpfile.js 代码

var base64 = require(''gulp-base64'');

gulp.task(''styles'', function() {
    return gulp.src(''src/css/*.css'') //源文件路径
        .pipe(base64()) //base64编码
        .pipe(gulp.dest(''dist/css'')) //目的路径
});

2.8 css压缩

通过gulp将css进行压缩

1 . 安装 gulp-minify-css

npm i gulp-minify-css --save-dev

2 . 编写 gulpfile.js 代码

var cssmin = require(''gulp-minify-css'');

gulp.task(''styles'', function() {
    return gulp.src(''src/css/*.css'') //源文件路径
        .pipe(cssmin()) //css压缩
        .pipe(gulp.dest(''dist/css'')) //目的路径
});

2.9 排列文件顺序

通过gulp将js调整前后顺序

1 . 安装 gulp-order

npm i gulp-order --save-dev

2 . 编写 gulpfile.js 代码

var order = require("gulp-order");

gulp.task(''scripts'', function() {
    return gulp.src(''src/js/*.js'')  //源文件路径
        .pipe(order([
            "src/js/config.js",
            "src/js/index.js"
        ]))
        .pipe(gulp.dest(''dist/js'')) //目的路径
})

2.10 合并文件

通过gulp将多个文件进行合并

1 . 安装 gulp-concat

npm i gulp-concat --save-dev

2 . 编写 gulpfile.js 代码

var concat = require(''gulp-concat'');

gulp.task(''scripts'', function() {
    return gulp.src(''src/js/*.js'')  //源文件路径
        .pipe(concat(''main.js''))  //合并文件
        .pipe(gulp.dest(''dist/js'')) //目的路径
})

2.11 重命名文件

通过gulp将文件名进行更改

1 . 安装 gulp-rename

npm i gulp-rename --save-dev

2 . 编写 gulpfile.js 代码

var rename = require(''gulp-rename'');

gulp.task(''scripts'', function() {
    return gulp.src(''src/js/*.js'')  //源文件路径
         .pipe(rename({  
              suffix: ''.min''
          }))   //修改文件名     
         .pipe(gulp.dest(''dist/js'')) //目的路径
})

2.12 JS文件压缩

通过gulp将js文件进行压缩

1 . 安装 gulp-uglify

npm i gulp-uglify --save-dev

2 . 编写 gulpfile.js 代码

var rename = require(''gulp-rename'');

gulp.task(''scripts'', function() {
    return gulp.src(''src/js/*.js'')  //源文件路径
         .pipe(uglify())   //压缩js
         .pipe(gulp.dest(''dist/js'')) //目的路径
})

2.13 图片压缩

通过gulp将图片进行压缩

1 . 安装 gulp-imagemin

npm i gulp-imagemin --save-dev

2 . 编写 gulpfile.js 代码

gulp.task(''images'', function() {
    return gulp.src(''src/img/*'')
        .pipe(cache(imagemin({
            optimizationLevel: 3,
            progressive: true,
            interlaced: true
        })))
        .pipe(gulp.dest(''dist/img''))
});

2.14 处理串行任务

定义多个任务的顺序执行关系,否则默认情况下,任务会以最大的并发数同时运行。

//清除任务
gulp.task(''clean'', function() {
    return gulp.src(''dist/css'', { read: false })
        .pipe(clean());
});

//编译任务
gulp.task(''styles'', function() {
    return gulp.src(''src/less/*.less'') //源文件路径
        .pipe(less()) //less编译                       
        .pipe(gulp.dest(''dist/css'')) //目的路径
});

//先清空目录,然后再执行编译CSS
gulp.task(''default'', [''clean''], function() {
    gulp.start(''styles'')
});

2.15 热加载服务

使用 BrowserSync 服务实现文件变更的实时编译调试

1 . 安装 browser-sync

npm i browser-sync --save-dev

2 . 编写 gulpfile.js 代码

var browserSync = require(''browser-sync'').create();

gulp.task(''dev'', function() {
    //初始化browser-sync服务
    browserSync.init({
        server: {
            baseDir: "./dist"
        }
    });
    
    //检测less文件是否更改,来调用重新编译css
    gulp.watch(''src/less/*'', [''styles'']);  

    //如果css文件更改过则刷新服务器
    gulp.watch( [''./dist/sys/css/*''] ).on("change", browserSync.reload)
});

--

3. Webpack 简介

WebPack可以看做是模块打包机:把你的项目当做一个整体,通过一个给定的主文件(如:index.js),从这个文件开始分析你的项目结构,找到项目的所有依赖文件,使用loaders处理它们,最后打包为一个浏览器可识别的JavaScript文件。

image.png

--

3.1 配置webpack

1 . 新建一个项目文件夹,并且安装webpack

mkdir webpack-demo && cd webpack-demo
npm init -y
npm install --save-dev webpack

2 . 新建html以及js文件如下

<html>
    <head>
        <title>webpack</title>
    </head>
    <body>
        <div></div>
        
        <script src="dist/bundle.js"></script>
    </body>
</html>
<!-- common.js -->
exports.printmsg = function(msg) {
    console.log(msg);
}

<!-- index.js -->
var lib = require(''./common.js'')
lib.printmsg(''good'')

3 . 编译webpack

webpack src/js/index.js dist/bundle.js

可以看到打包结果如下:

$ webpack src/js/index.js dist/bundle.js
Hash: 39e1d99d27c58dd34eb1
Version: webpack 2.5.1
Time: 81ms
    Asset     Size  Chunks             Chunk Names
bundle.js  2.82 kB       0  [emitted]  main
   [0] ./src/js/common.js 58 bytes {0} [built]
   [1] ./src/js/index.js 50 bytes {0} [built]

项目结构如下:

image.png

--

3.2 编写配置文件

Webpack拥有很多高级的功能,这些功能其实都可以通过命令行模式实现,但是正如已经提到的,这样不太方便且容易出错的,一个更好的办法是定义一个配置文件,这个配置文件其实也是一个简单的JavaScript模块,可以把所有的与构建相关的信息放在里面。下面来说明如何定义一个配置文件:

1 . 在根目录下面新建 webpack.config.js

var path = require(''path'');

module.exports = {
  entry: ''./src/js/index.js'',
  output: {
    filename: ''bundle.js'',
    path: path.resolve(__dirname, ''dist'')
  }
};

2 . 修改 package.json,添加条目如下

{
  ...
  "scripts": {
    "build": "webpack",
  },
  ...
}

3 . 使用命令行编译项目

npm run build

--

3.3 调试webpack

开发总是离不开调试,如果可以更加方便的调试当然就能提高开发效率,不过打包后的文件有时候你是不容易找到出错了的地方对应的源代码的位置的,Source Maps就是来帮我们解决这个问题的。通过简单的配置后,Webpack在打包时可以为我们生成的source maps,这为我们提供了一种对应编译文件和源文件的方法,使得编译后的代码可读性更高,也更容易调试。

devtool选项 配置结果
source-map 在一个单独的文件中产生一个完整且功能完全的文件。这个文件具有最好的source map,但是它会减慢打包文件的构建速度;
cheap-module-source-map 在一个单独的文件中生成一个不带列映射的map,不带列映射提高项目构建速度,但是也使得浏览器开发者工具只能对应到具体的行,不能对应到具体的列(符号),会对调试造成不便;
eval-source-map 使用eval打包源文件模块,在同一个文件中生成干净的完整的source map。这个选项可以在不影响构建速度的前提下生成完整的sourcemap,但是对打包后输出的JS文件的执行具有性能和安全的隐患。不过在开发阶段这是一个非常好的选项,但是在生产阶段一定不要用这个选项;
cheap-module-eval-source-map 这是在打包文件时最快的生成source map的方法,生成的Source Map 会和打包后的JavaScript文件同行显示,没有列映射,和eval-source-map选项具有相似的缺点;

在学习阶段以及在小到中性的项目上,eval-source-map 是一个很好的选项,不过记得只在开发阶段使用它,继续上面的例子,进行如下配置

var path = require(''path'');
module.exports = {
  entry: ''./src/js/index.js'',
  output: {
    filename: ''bundle.js'',
    path: path.resolve(__dirname, ''dist'')
  },
  devtool: ''eval-source-map''
};

--

3.4 建立本地开发服务器

Webpack提供一个可选的本地开发服务器,这个本地服务器基于node.js构建,可以实现代码的热加载功能,可以通过它方便的进行代码的开发。其构建方法如下:

1 . 安装 webpack-dev-server

npm install --save-dev webpack-dev-server

2 . 修改配置文件 webpack.config.js

var path = require(''path'');
module.exports = {
    entry: ''./src/js/index.js'',
    output: {
        filename: ''bundle.js'',
        path: path.resolve(__dirname, ''dist'')
    },
    devServer: {
        contentBase: "./",
        port: 9000,
        inline: true
    }
};

3 . 修改 package.json,添加条目如下

{
  ...
  "scripts": {
    "dev": "webpack-dev-server",
  },
  ...
}

4 . 输入 npm run dev 启动 webpack-dev-server

$ npm run dev                                                                            
                                                                                         
> webpackproj@1.0.0 dev F:\Project\DEMO\webpackdemo                                      
> webpack-dev-server                                                                     
                                                                                         
Project is running at http://localhost:9000/                                             
webpack output is served from /                                                          
Content not from webpack is served from ./                                               
Hash: 1aca755d21fcb2c76314                                                               
Version: webpack 2.5.1                                                                   
Time: 918ms                                                                              
        Asset    Size  Chunks                    Chunk Names                             
    bundle.js  316 kB       0  [emitted]  [big]  main                                    
bundle.js.map  375 kB       0  [emitted]         main                                    
chunk    {0} bundle.js, bundle.js.map (main) 302 kB [entry] [rendered]                   
   [35] (webpack)-dev-server/client?http://localhost:9000 5.68 kB {0} [built]            
   [36] ./src/js/index.js 69 bytes {0} [built]                                           
   [37] ./~/ansi-html/index.js 4.26 kB {0} [built]                                       
   [38] ./~/ansi-regex/index.js 135 bytes {0} [built]                                    
   [40] ./~/events/events.js 8.33 kB {0} [built]                                         
   [41] ./~/html-entities/index.js 231 bytes {0} [built]                                 
   [48] ./~/querystring-es3/index.js 127 bytes {0} [built]                               
   [76] ./~/strip-ansi/index.js 161 bytes {0} [built]                                    
   [78] ./~/url/url.js 23.3 kB {0} [built]                                               
   [79] ./~/url/util.js 314 bytes {0} [built]                                            
   [80] (webpack)-dev-server/client/overlay.js 3.73 kB {0} [built]                       
   [81] (webpack)-dev-server/client/socket.js 897 bytes {0} [built]                      
   [83] (webpack)/hot/emitter.js 77 bytes {0} [built]                                    
   [84] ./src/js/common.js 58 bytes {0} [built]                                          
   [85] multi (webpack)-dev-server/client?http://localhost:9000 ./src/js/index.js 40 byte
s {0} [built]                                                                            
     + 71 hidden modules                                                                 
webpack: Compiled successfully.                                                                                                                                  

--

3.5 配置HTML代码热加载

webpack-dev-server 只能监控入口文件(JS/LESS/CSS/IMG)的变化,因此 HTML文件的变化必须依赖插件来进行监控。

1 . 安装 html-webpack-plugin

npm install html-webpack-plugin --save-dev

2 . 修改配置文件 webpack.config.js, 把 index.html 加入监控

var path = require(''path'');
var HtmlWebpackPlugin = require(''html-webpack-plugin'')

module.exports = {
    entry: ''./src/js/index.js'',
    output: {
        filename: ''bundle.js'',
        path: path.resolve(__dirname, ''dist'')
    },
    plugins: [
        new HtmlWebpackPlugin({   // html代码热加载
            template: ''./index.html''
        }),
    ],
    devServer: {
        contentBase: "./",
        port: 9000,
        inline: true
    }
};

此时可以取消 html 文件内的 js 引用,因为 html-webpack-plugin 会自动加载编译完的 js 文件

--

3.6 配置自动打开浏览器

通过配置 open-browser-webpack-plugin 可以在webpack编译完之后自动打开浏览器;

1 . 安装 open-browser-webpack-plugin

npm install open-browser-webpack-plugin --save-dev

2 . 修改配置文件 webpack.config.js

var path = require(''path'');
var HtmlWebpackPlugin = require(''html-webpack-plugin'')
var OpenBrowserPlugin = require(''open-browser-webpack-plugin'');

module.exports = {
    entry: ''./src/js/index.js'',
    output: {
        filename: ''bundle.js'',
        path: path.resolve(__dirname, ''dist'')
    },
    plugins: [
        new HtmlWebpackPlugin({ // html代码热加载
            template: ''./index.html''
        }),
        new OpenBrowserPlugin({ //自动打开浏览器
            url: ''http://localhost:9000''
        })
    ],
    devServer: {
        contentBase: "./",
        port: 9000,
        inline: true
    }
};

--

3.7 配置 json 加载器

使用 json 解析器可以将常量数据定义在 json文件中,然后在 js 文件中调用。

1 . 在项目根目录下面创建 config.json 文件,内容如下

{
    "name": "demo",
    "type": "HTML5"
}

2 . 修改 index.js

var config = require(''../../config.json'')
var lib = require(''./common.js'')
lib.printmsg(config.name)

3 . 修改配置文件 webpack.config.js

var path = require(''path'');

module.exports = {
    entry: ''./src/js/index.js'',
    output: {
        filename: ''bundle.js'',
        path: path.resolve(__dirname, ''dist'')
    },
    module: { 
        rules: [{
            test: /\.json$/,
            loader: "json-loader"
        }]
    }
};

项目结构如下:

image.png

--

3.8 配置 LESS 编译

1 . 安装 less style-loader css-loader less-loader

npm install less style-loader css-loader less-loader --save-dev

2 . 在项目的css目录下面创建 index.less 文件,内容如下

@charset "utf-8";
@gray-base:  #000;
@gray-light:  lighten(@gray-base, 46.7%); 

.g-index {
    height: 100vh;
    background: @gray-light;
}

3 . 修改 index.js

require(''../css/index.less'')

var lib = require(''./common.js'')
lib.printmsg(''good'')

4 . 修改配置文件 webpack.config.js

var path = require(''path'');

module.exports = {
    devtool: ''source-map'',
    entry: ''./src/js/index.js'',
    output: {
        filename: ''bundle.js'',
        path: path.resolve(__dirname, ''dist'')
    },
    module: { 
        rules: [
        {
            test: /\.less$/, // less解析器
            loader: ''style-loader!css-loader!less-loader''
        },]
    }
};

项目结构如下:

image.png

--

3.9 配置 Babel 编译

1 . 安装 babel-core babel-loader babel-preset-es2015

 npm install babel-core babel-loader babel-preset-es2015 --save-dev

2 . 修改 common.jsES6 格式

exports.printmsg = (msg) => {
    console.log(msg);
}

3 . 修改配置文件 webpack.config.js

var path = require(''path'');
module.exports = {
    devtool: ''source-map'',
    entry: ''./src/js/index.js'',
    output: {
        filename: ''bundle.js'',
        path: path.resolve(__dirname, ''dist'')
    },
    module: { 
        rules: [{
            test: /\.js$/,   //babel解析器
            exclude: /node_modules/,
            loader: ''babel-loader'',
            query: {
                presets: [''es2015'']
            }
        }]
    }
};

--

3.10 配置 jQuery 解析器

1 . 安装 jquery

 npm install jquery --save-dev

2 . 修改 index.js 调用 jquery 函数

require(''jquery'')

$(init)
function init() {
    var lib = require(''./common.js'')
    lib.printmsg(''good'')
}

3 . 修改配置文件 webpack.config.js

var path = require(''path'');
var webpack = require(''webpack'');

module.exports = {
    devtool: ''source-map'',
    entry: ''./src/js/index.js'',
    output: {
        filename: ''bundle.js'',
        path: path.resolve(__dirname, ''dist'')
    },
    module: {   
        rules: [{
            test: /\.js$/,  //babel代码解析
            exclude: /node_modules/,
            loader: ''babel-loader'',
            query: {
                presets: [''es2015'']
            }
        }]
    },
    plugins: [
        new webpack.ProvidePlugin({   //jquery解析器
            $: "jquery",
            jQuery: "jquery",
            "window.jQuery": "jquery"
        })
    ]
};

--

3.11 配置 js 代码压缩

1 . 修改配置文件 webpack.config.js, 在 plugin 中添加 webpack.optimize.UglifyJsPlugin 模块

var path = require(''path'');
var webpack = require(''webpack'');
var uglifyJsPlugin = webpack.optimize.UglifyJsPlugin;

module.exports = {
    devtool: ''source-map'',
    entry: ''./src/js/index.js'',
    output: {
        filename: ''bundle.js'',
        path: path.resolve(__dirname, ''dist'')
    },
    module: { //在配置文件里添加JSON loader
        rules: [{
            test: /\.js$/,   //babel代码解析
            exclude: /node_modules/,
            loader: ''babel-loader'',
            query: {
                presets: [''es2015'']
            }
        }]
    },
    plugins: [
         new uglifyJsPlugin({ //js代码压缩
            compress: {
                warnings: false
            }
        })
    ]
};

--

3.12 配置 eslint 语法解析

1 . 安装 esline

 npm install eslint eslint-loader eslint-friendly-formatter eslint-plugin-html babel-eslint eslint-config-standard eslint-plugin-import eslint-plugin-node eslint-plugin-promise eslint-plugin-standard --save-dev

2 . 在项目根目录下添加eslint 配置文件.eslintrc.js

// http://eslint.org/docs/user-guide/configuring
module.exports = {
  root: true,
  parser: ''babel-eslint'',
  parserOptions: {
    sourceType: ''module''
  },
  env: {
    browser: true,
  },
  // https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style
  extends: ''standard'',
  // required to lint *.vue files
  plugins: [
    ''html''
  ],
  // add your custom rules here
  ''rules'': {
    // allow paren-less arrow functions
    ''arrow-parens'': 0,
    "indent": [2, 4],//缩进风格
    ''no-undef'': 0,
    // allow async-await
    ''generator-star-spacing'': 0,
    // allow debugger during development
    ''no-debugger'': process.env.NODE_ENV === ''production'' ? 2 : 0
  }
}

3 . 修改配置文件 webpack.config.js

var path = require(''path'');

module.exports = {
    devtool: ''source-map'',
    entry: ''./src/js/index.js'',
    output: {
        filename: ''bundle.js'',
        path: path.resolve(__dirname, ''dist'')
    },
    module: { 
        rules: [{
            test: /\.js$/,   //babel代码解析
            exclude: /node_modules/,
            loader: ''babel-loader'',
            query: {
                presets: [''es2015'']
            }
        }, {
            test: /\.js$/, //eslint语法解析
            exclude: /node_modules/,
            loader: ''eslint-loader'',
            enforce: ''pre'',
            options: {
                formatter: require(''eslint-friendly-formatter'')
            }
        }]
    }
};

--

3.13 配置图片压缩器

1 . 安装 url-loader

 npm install url-loader --save-dev

2 . 修改 index.less 文件

@charset "utf-8";
@gray-base:  #000;
@gray-light:  lighten(@gray-base, 46.7%); 

.g-index {
    height: 100vh;
    background: @gray-light;
    background: url(''../img/small.png'') no-repeat;
}

3 . 修改配置文件 webpack.config.js

var path = require(''path'');
var webpack = require(''webpack'');

module.exports = {
    devtool: ''source-map'',
    entry: ''./src/js/index.js'',
    output: {
        filename: ''bundle.js'',
        path: path.resolve(__dirname, ''dist'')
    },
    module: {
        rules: [ {
            test: /\.less$/, // less解析器
            loader: ''style-loader!css-loader!less-loader''
        }, {
            test: /\.(png|jpg)$/, // img压缩器
            loader: ''url-loader?limit=8192''
        }]
    }

项目结构如下:

image.png

 

--

3.14 配置公共库抽取

1 . 安装 chunk-manifest-webpack-plugin webpack-chunk-hash

 npm install chunk-manifest-webpack-plugin webpack-chunk-hash  --save-dev

3 . 修改配置文件 webpack.config.js

var path = require(''path'');
var webpack = require(''webpack'');
var HtmlWebpackPlugin = require(''html-webpack-plugin'')
var WebpackChunkHash = require("webpack-chunk-hash");
var ChunkManifestPlugin = require("chunk-manifest-webpack-plugin");

module.exports = {
    devtool: ''source-map'',
    entry: { 
        main: ''./src/js/index.js'',
        vendor: [''jquery'']
    },
    output: {
        filename: ''[name].[chunkhash].js'',
        path: path.resolve(__dirname, ''dist'')
    },
    module: {
        rules: [ {
            test: /\.less$/, // less解析器
            loader: ''style-loader!css-loader!less-loader''
        }, {
            test: /\.(png|jpg)$/, // img压缩器
            loader: ''url-loader?limit=8192''
        }]
    },
    plugins: [
        new HtmlWebpackPlugin({ // html代码热加载
            template: ''./index.html''
        }),
        new webpack.ProvidePlugin({ //jquery解析器
            $: "jquery",
            jQuery: "jquery",
            "window.jQuery": "jquery"
        }),
        new webpack.optimize.CommonsChunkPlugin({  //公共库抽取
            name: ["vendor", "manifest"], // vendor libs + extracted manifest
            minChunks: Infinity,
        }),
        new webpack.HashedModuleIdsPlugin(),
        new WebpackChunkHash(),
        new ChunkManifestPlugin({
          filename: "chunk-manifest.json",
          manifestVariable: "webpackManifest"
        })
    ]
};

--

3.15 配置模块分析器

在项目复杂的情况下,为了分析多个模块的相互依赖以及打包的关系,通常引入模块打包分析工具,可以清晰的给出每个模块的依赖关系。

1 . 安装 webpack-bundle-analyzer

 npm install webpack-bundle-analyzer  --save-dev

2 . 修改配置文件 webpack.config.js

var path = require(''path'');
var { BundleAnalyzerPlugin }  = require(''webpack-bundle-analyzer'')

module.exports = {
    devtool: ''source-map'',
    entry: { 
        main: ''./src/js/index.js'',
        vendor: [''jquery'']
    },
    output: {
        filename: ''[name].[chunkhash].js'',
        path: path.resolve(__dirname, ''dist'')
    },
    plugins: [
        new BundleAnalyzerPlugin()
    ]
};


 

React+TypeScript+webpack4多入口配置详解

React+TypeScript+webpack4多入口配置详解

资源

  • React-16.8.*
  • react-router-dom-4.3.*
  • TypeScript-3.5.*
  • webpack-4.*
  • eslint-5.16.*

项目目录

├── dist # 打包结果目录
│  ├── demo1 //类别demo1的打包结果
│  │  ├── demo1.himl
│  │  ├── demo1.js
│  │  └── demo1.css
│  └── demo2 ... //类别demo2的打包结果
├── src # 业务资源文件目录
│  ├── category //项目分类
│  │  ├── demo1
│  │  ├── demo2
│  │  └── ...
│  ├── components //公共组件
│  ├── util //公共资源
│  └── custom.d.ts //项目全局变量声明文件
├── index.html //项目启动入口
├── .gitignore //git忽略文件
├── .eslintrc.js //eslint校验配置
├── package.json //依赖包
├── tsconfig.json //ts配置
├── webpack.config.build.js //webpack打包
├── webpack.config.base.js //webpack基础配置
└── webpack.config.js //项目启动配置

前言

对于复杂或多人开发的 React 项目来说,管理和使用每个组件的 props 、 state 或许会成为一件让人头痛的事情,而为每一个组件写文档,成本也会比较大,对项目的开发效率也不是最理想的。

Typescript 给 React 带来很多好处:

  • 在组件头部定义 interface,让每个人在拿到组件的第一时间就可以很明确知道该组件需要使用的 props 和 state;
  • 在编译中发现问题,减少运行时的报错;
  • 可以在编辑器中实现实时类型校验、引用查询;
  • 约束类型,在混合多语言环境中降低风险,等。

需求

要搭建一个React+TypeScript+webpack的项目的话,一般都是团队开发多人多文件项目,在搭建之前需要优先考虑以下几个方面:

  • 开发体验
  • 项目打包
  • 团队规范

安装

前置安装

首先需要全局安装typescript,这里默认大家都已经安装了node以及npm

npm install -g typescript

首先新建文件夹并进入

mkdir tsDemo && cd tsDemo

然后进行初始化,生成package.json和tsconfig.json

npm init -y && tsc --init

安装开发工具

npm install-D webpack webpack-cli webpack-dev-server

安装react相关

因为需要整合ts,而react原本的包是不包含验证包的,所以这里也需要安装相关ts验证包

npm install -S react react-dom
npm install -D @types/react @types/react-dom

安装ts-loader

npm install -D ts-loader

以上是基本的 后续会贴出项目demo里面包含所有依赖包

webpack配置

添加webpack文件

根目录下新建webpack.config.base.js、webpack.config.build.js、webpack.config.js文件

touch webpack.config.base.js webpack.config.build.js webpack.config.js
  • entry:入口文件(你要打包,就告诉我打包哪些)
  • output:出口文件(我打包完了,给你放到哪里)
  • resolve: 寻找模块所对应的文件
  • module:模块(放lorder,编译浏览器不认识的东西)
  • plugins:插件(辅助开发,提高开发效率)
  • externals:打包忽略
  • devServer:服务器(webpack提供的本地服务器)
  • mode:模式,分为开发模式、生产模式。此为4.X里新增的

配置entry入口文件

因为大部分项目是多入口,多类别的,所有入口配置时不要配置单一入口

const fs = require("fs");
const path = require("path");
const optimist = require("optimist");

const cateName = optimist.argv.cate;
let entryObj = {};
const srcPath = `${__dirname}/src`;
//获取当前项目要启动或者打包的基础路径
const entryPath = `${srcPath}/category/`;
//未指定类别 启动或者打包所有类别
//如:npm run dev 或者npm run build
if (cateName == true) {
  fs.readdirSync(entryPath).forEach((cateName, index) => {
    // cateName/cateName指定输出路径为entryname
    if (cateName != "index.html" && cateName != ".DS_Store") entryObj[`${cateName}/${cateName}`] = `${entryPath + cateName}/${cateName}.tsx`;
  });
} else if (cateName.indexOf(",")) {
  // 一次指定多个类别 类别之间以","分割
  //如:npm run dev erhsouche,huoche 
  let cateNameArray = cateName.split(",");
  for (let i = 0; i < cateNameArray.length; i++) {
    entryObj[`${cateNameArray[i]}/${cateNameArray[i]}`] = `${entryPath + cateNameArray[i]}/${
      cateNameArray[i]
    }.tsx`;
  }
} else {
  // 打包单个入口文件
  //如:npm run dev ershouche
  entryObj[`${cateName}/${cateName}`] = `${entryPath + cateName}/${cateName}.tsx`;
}
const webpackConfig = {
  entry: entryObj,
}
module.exports = {
  webpackConfig,
  entryObj
};

配置output出口文件

const webpackConfig = {
  output: {
    //输出文件名称以当前传入的cate类别名称命名
    filename: "[name].js", 
    //输出到根目录下的dist目录中
    path: path.resolve(__dirname, "dist"),
    publicPath: "/",
  },
}

配置resolve

需要import xxx from ''xxx''这样的文件的话需要在webpack中的resolve项中配置extensions,这样以后引入文件就不需要带扩展名

const webpackConfig = {
  resolve: {
    extensions: [".tsx", ".ts", ".js", ".jsx", ".json"],
    //配置项通过别名来把原导入路径映射成一个新的导入路径。
    alias: {
      images: path.join(__dirname, "src/util/img")
    },
    // 使用绝对路径指明第三方模块存放的位置,以减少搜索步骤
    modules: [path.resolve(__dirname, "node_modules")] 
  },
}

配置module

概念

在webpack中任何一个东西都称为模块,js就不用说了。一个css文件,一张图片、一个less文件都是一个模块,都能用导入模块的语法(commonjs的require,ES6的import)导入进来。webpack自身只能读懂js类型的文件,其它的都不认识。但是webpack却能编译打包其它类型的文件,像ES6、JSX、less、typeScript等,甚至css、images也是Ok的,而想要编译打包这些文件就需要借助loader

loader就像是一个翻译员,浏览器不是不认识这些东西么?那好交给loader来办,它能把这些东西都翻译成浏览器认识的语言。loader描述了webpack如何处理非js模块,而这些模块想要打包loader必不可少,所以它在webpack里显得异常重要。loader跟插件一样都是模块,想要用它需要先安装它,使用的时候把它放在module.rules参数里,rules翻译过来的意思就是规则,所以也可以认为loader就是一个用来处理不同文件的规则

所需loader

ts-loader

编译TypeScript文件

npm install ts-loader -D

url-loader

处理css中的图片资源时,我们常用的两种loader是file-loader或者url-loader,两者的主要差异在于。url-loader可以设置图片大小限制,当图片超过限制时,其表现行为等同于file-loader,而当图片不超过限制时,则会将图片以base64的形式打包进css文件,以减少请求次数。

npm install url-loader -D

css处理所需loader
css-loader 处理css
sass-loader 编译处理scss
sass-resources-loader 全局注册变量

html-loader
处理.html文件

module完整配置

const webpackConfig = {
  module: {
    rules: [
      //处理tsx文件
      { test: /\.(tsx|ts)?$/, use: ["ts-loader"], include: path.resolve(__dirname, "src") },
      //处理图片资源
      {
        test: /\.(png|jpe?g|jpg|gif|woff|eot|ttf|svg)/,
        use: [
          // 对非文本文件采用file-loader加载
          {
            loader: "url-loader",
            options: {
              limit: 1024 * 30, // 30KB以下的文件
              name: "images/[name].[hash:8].[ext]",
            }
          }
        ],
      },
      //处理css和scss
      {
        test: /\.(css|scss)$/,
        use: [
          //css单独打包
          MiniCssExtractPlugin.loader,
          {
            loader: "css-loader"
          },
          {
            loader: "postcss-loader",
            options: {
              plugins: () => [require("autoprefixer")],
              sourceMap: true
            }
          },
          {
            loader: "sass-loader",
            options: {
              sourceMap: true
            }
          },
          {
            loader: "sass-resources-loader",
            options: {
              resources: ["./skin/mixin.scss", "./skin/base.scss"]
            }
          }
        ],
        exclude: path.resolve(__dirname, "node_modules")
      },
      {
        test: /\.html$/,
        use: {
          loader: "html-loader",
        }
      },
      { test: /src\/containers(\/.*).(tsx|ts)/, loader: "bundle-loader?lazy!ts-loader" },
      { enforce: "pre", test: /\.js$/, loader: "source-map-loader" }
    ]
  },
}

配置plugins

plugins里面放的是插件,插件的作用在于提高开发效率,能够解放双手,让我们去做更多有意义的事情。一些很low的事就统统交给插件去完成。

const webpackConfig = {
  plugins: [
    //清除文件
    new CleanWebpackPlugin(),
    //css单独打包
    new MiniCssExtractPlugin({
      filename: "[name].css",
      chunkFilename: "[name].css"
    }),
    // 引入热更新插件
    new webpack.HotModuleReplacementPlugin() 
  ]
}

配置externals

如果需要引用一个库,但是又不想让webpack打包(减少打包的时间),并且又不影响我们在程序中以CMD、AMD或者window/global全局等方式进行使用(一般都以import方式引用使用),那就可以通过配置externals。

const webpackConfig = {
  //项目编译打包是忽略这些依赖包
  externals: {
    react: "React",
    "react-dom": "ReactDOM",
    "react-redux": "ReactRedux",
  }
}

配置mode

mode是webpack4新增的一条属性,它的意思为当前开发的环境。mode的到来减少了很多的配置,它内置了很多的功能。相较以前的版本提升了很多,减少了很多专门的配置

  • 提升了构建速度
  • 默认为开发环境,不需要专门配置
  • 提供压缩功能,不需要借助插件
  • 提供SouceMap,不需要专门配置

mode分为两种环境,一种是开发环境(development),一种是生产环境(production)。开发环境就是我们写代码的环境,生产环境就是代码放到线上的环境。这两种环境的最直观区别就是,开发环境的代码不提供压缩,生产环境的代码提供压缩。

配置devServer

const webpackConfig = {
  devServer: {
    // 本地服务器所加载的页面所在的目录
    contentBase: srcPath, 
    //热更新
    hot: true,
    //服务端口
    port: "7788",
    // 是否向Chunk中注入代理客户端,默认注入
    inline: true, 
    // publicPath: ''/dist/'',
    historyApiFallback: {
      index: "template.html",
    },
    //默认检查hostname
    disableHostCheck: true,
    compress: true,
    open: true // 自动打开首页
  }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

您可能感兴趣的文章:
  • React+Webpack快速上手指南(小结)
  • 从零搭建Webpack5-react脚手架的实现步骤(附源码)
  • 使用webpack配置react-hot-loader热加载局部更新

The way of Webpack learning (I.) -- Configure Webpack from zero(从零开始配置webpack)

The way of Webpack learning (I.) -- Configure Webpack from zero(从零开始配置webpack)

学习之路基于webpack3.10.0,webpack4.0之后更新。

一:开始前的配置

1、初始化项目,其实就是新建一个package.json文件,后面的命令依赖里面的配置项。

npm init

2、修改npm script定义的任务,新增一项。

"scripts": {
    "start": "webpack --config webpack.config.js"
}

3、安装webpack

npm i -D webpack@3.10.0

 注:--save --dev 和 --save的区别?

答:--save --dev是开发环境需要的包,添加到devDependencies里面。

--save是生产环境需要的包,添加到dependencies里面。

 

二:使用webpack打包Js文件

1、页面入口文件 index.html

<html>
<head>
  <meta charset="UTF-8">
</head>
<body>
  <div id="app"></div>
  <!--导入 Webpack 输出的 JavaScript 文件-->
  <script src="./dist/bundle.js"></script>
</body>
</html>

2、JS 工具函数文件 show.js

// 操作 DOM 元素,把 content 显示到网页上
function show(content) {
  window.document.getElementById(''app'').innerText = ''Hello,'' + content;
}

// 通过 CommonJS 规范导出 show 函数
module.exports = show;

3、JS 执行入口文件 main.js

// 通过 CommonJS 规范导入 show 函数
const show = require(''./show.js'');
// 执行 show 函数
show(''Webpack'');

4、Webpack 在执行构建时默认会从项目根目录下的 webpack.config.js 文件读取配置,所以你还需要新建它,其内容如下:

const path = require(''path'');

module.exports = {
  // JavaScript 执行入口文件
  entry: ''./main.js'',
  output: {
    // 把所有依赖的模块合并输出到一个 bundle.js 文件
    filename: ''bundle.js'',
    // 输出文件都放到 dist 目录下
    path: path.resolve(__dirname, ''./dist''),//path.resolve() 方法会把一个路径或路径片段的序列解析为一个绝对路径。
}
};

5、打包js文件

npm start

一切文件就绪,在项目根目录下执行 webpack 命令运行 Webpack 构建,你会发现目录下多出一个 dist目录,里面有个 bundle.js 文件, bundle.js 文件是一个可执行的 JavaScript 文件。




关于Webpack 配置详解及实现过程webpack简单配置的问题我们已经讲解完毕,感谢您的阅读,如果还想了解更多关于02-webpack的基本配置和输入webpack实现自动打包、Gulp & webpack 配置详解、React+TypeScript+webpack4多入口配置详解、The way of Webpack learning (I.) -- Configure Webpack from zero(从零开始配置webpack)等相关内容,可以在本站寻找。

本文标签: