其实只要在最终的html页面中加载了插件,任何component.ts的生命周期内部都可以直接用正确语法(比如改写var成let)抄写普通的js,new一个插件什么的,只是编译时会报找不到变量的红字,编译后完全functional...
我是在组件ts里面写require的,只要不报没有这个module,定义一下用到的变量后终于把找不到$和Swiper的红字给消了。
let $ = require('jquery'); let Swiper = require('Swiper');
这个require要成功必须在webpack.config中设置
module.exports = {
externals:[ { $:"jQuery" }, { jQuery:"jQuery" }, { window: "window"}, { Swiper: "Swiper"}, ],
如果是index.html里面<script>了jquery和swiper的话就不需用到loader(是否只是避免了编译时报错而并没有实际获取到jquery对象)
var webpack = require('webpack'); var autoprefixer = require('autoprefixer'); var HtmlWebpackPlugin = require('html-webpack-plugin'); var ExtractTextPlugin = require('extract-text-webpack-plugin'); module.exports = { //devtool : 'inline-source-map', entry: { main:['./app/main.ts', './app/sass/main.scss', './app/sass/home.scss', './app/sass/personal.scss', ], vendor:[ ] }, externals:[ { $:"jQuery" }, { jQuery:"jQuery" }, { window: "window"}, { Swiper: "Swiper"}, ], output: { path: './dist', filename: 'js/app.bundle.js', publicPath:'/', libraryTarget: "var" }, module: { loaders: [ {test: /.ts$/, loader: 'ts'}, {test: /.html$/, loader: 'raw'}, {test: /.css$/, loader: ExtractTextPlugin.extract('style', 'css!postcss')}, {test: /.scss$/, loader: ExtractTextPlugin.extract("style-loader", "css-loader!sass-loader")}, { test: require.resolve("./dist/assets/js/jquery.min.js"), loader: "imports-loader?jQuery=>jquery" }, { test: require.resolve("./dist/assets/js/swiper.jquery.js"), loader: 'imports-loader?Swiper=>swiper' } ] }, postcss: [autoprefixer()], resolve: { extensions: ['', '.js', '.ts', '.scss'], //alias: { jquery: "assets/js/jquery.min.js"} }, plugins: [ new ExtractTextPlugin("assets/css/[name].css", {publicPath: '/assets/',allChunks: true}), new HtmlWebpackPlugin({template: './app/index.html'}), new webpack.optimize.CommonsChunkPlugin({ name: "vendor", //filename : 'vendor_[chunkhash].js', filename : 'js/vendor.js', minChunks: Infinity }), new webpack.ProvidePlugin({ // $: "jquery", // jQuery: "jquery", // "window.jQuery": "jquery" }), /*new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }),*/ new webpack.DefinePlugin({ app: { environment: JSON.stringify(process.env.APP_ENVIRONMENT || 'development') } }) ] };
上面是用的 imports,换做expose:
{ test: require.resolve("./dist/assets/js/jquery.min.js"), loader: "expose?$!expose?jQuery" }, { test: require.resolve("./dist/assets/js/swiper.jquery.js"), loader: "expose?Swiper" }
为了达到按需加载的目的...但是loader总也不起作用呀!是RP问题吗?
我曾经试着在entry里面像import上面那些scss那样引入要用的插件,实验证明完全没有意义:
1.不能激活loader制造模块
2.不能代替html中引用script
3.不能使得ts正确import到模块
4.不能使得ts正确require到模块
查了那么多资料最终还是离不开直接在html里面引用html...
后来用到有一段扩展jquery的代码
// JavaScript Document /* * award Rotate - jQuery plugin for award Rotate */ (function($) { var supportedCSS,styles=document.getElementsByTagName("head")[0].style,toCheck="transformProperty WebkitTransform OTransform msTransform MozTransform".split(" "); for (var a=0;a<toCheck.length;a++) if (styles[toCheck[a]] !== undefined) supportedCSS = toCheck[a]; // Bad eval to preven google closure to remove it from code o_O // After compresion replace it back to var IE = 'v' == 'v' var IE = eval('"v"=="v"'); jQuery.fn.extend({ rotate:function(parameters) { if (this.length===0||typeof parameters=="undefined") return; if (typeof parameters=="number") parameters={angle:parameters}; var returned=[]; for (var i=0,i0=this.length;i<i0;i++) { var element=this.get(i); if (!element.Wilq32 || !element.Wilq32.PhotoEffect) { var paramClone = $.extend(true, {}, parameters); var newRotObject = new Wilq32.PhotoEffect(element,paramClone)._rootObj; returned.push($(newRotObject)); } else { element.Wilq32.PhotoEffect._handleRotation(parameters); } } return returned; }, getRotateAngle: function(){ var ret = []; for (var i=0,i0=this.length;i<i0;i++) { var element=this.get(i); if (element.Wilq32 && element.Wilq32.PhotoEffect) { ret[i] = element.Wilq32.PhotoEffect._angle; } } return ret; }, stopRotate: function(){ for (var i=0,i0=this.length;i<i0;i++) { var element=this.get(i); if (element.Wilq32 && element.Wilq32.PhotoEffect) { clearTimeout(element.Wilq32.PhotoEffect._timer); } } } }); // Library agnostic interface Wilq32=window.Wilq32||{}; Wilq32.PhotoEffect=(function(){ if (supportedCSS) { return function(img,parameters){ img.Wilq32 = { PhotoEffect: this }; this._img = this._rootObj = this._eventObj = img; this._handleRotation(parameters); } } else { return function(img,parameters) { // Make sure that class and id are also copied - just in case you would like to refeer to an newly created object this._img = img; this._rootObj=document.createElement('span'); this._rootObj.style.display="inline-block"; this._rootObj.Wilq32 = { PhotoEffect: this }; img.parentNode.insertBefore(this._rootObj,img); if (img.complete) { this._Loader(parameters); } else { var self=this; // TODO: Remove $ dependency $(this._img).bind("load", function() { self._Loader(parameters); }); } } } })(); Wilq32.PhotoEffect.prototype={ _setupParameters : function (parameters){ this._parameters = this._parameters || {}; if (typeof this._angle !== "number") this._angle = 0 ; if (typeof parameters.angle==="number") this._angle = parameters.angle; this._parameters.animateTo = (typeof parameters.animateTo==="number") ? (parameters.animateTo) : (this._angle); this._parameters.step = parameters.step || this._parameters.step || null; this._parameters.easing = parameters.easing || this._parameters.easing || function (x, t, b, c, d) { return -c * ((t=t/d-1)*t*t*t - 1) + b; } this._parameters.duration = parameters.duration || this._parameters.duration || 1000; this._parameters.callback = parameters.callback || this._parameters.callback || function(){}; if (parameters.bind && parameters.bind != this._parameters.bind) this._BindEvents(parameters.bind); }, _handleRotation : function(parameters){ this._setupParameters(parameters); if (this._angle==this._parameters.animateTo) { this._rotate(this._angle); } else { this._animateStart(); } }, _BindEvents:function(events){ if (events && this._eventObj) { // Unbinding previous Events if (this._parameters.bind){ var oldEvents = this._parameters.bind; for (var a in oldEvents) if (oldEvents.hasOwnProperty(a)) // TODO: Remove $ dependency $(this._eventObj).unbind(a,oldEvents[a]); } this._parameters.bind = events; for (var a in events) if (events.hasOwnProperty(a)) // TODO: Remove $ dependency $(this._eventObj).bind(a,events[a]); } }, _Loader:(function() { if (IE) return function(parameters) { var width=this._img.width; var height=this._img.height; this._img.parentNode.removeChild(this._img); this._vimage = this.createVMLNode('image'); this._vimage.src=this._img.src; this._vimage.style.height=height+"px"; this._vimage.style.width=width+"px"; this._vimage.style.position="absolute"; // FIXES IE PROBLEM - its only rendered if its on absolute position! this._vimage.style.top = "0px"; this._vimage.style.left = "0px"; /* Group minifying a small 1px precision problem when rotating object */ this._container = this.createVMLNode('group'); this._container.style.width=width; this._container.style.height=height; this._container.style.position="absolute"; this._container.setAttribute('coordsize',width-1+','+(height-1)); // This -1, -1 trying to fix ugly problem with small displacement on IE this._container.appendChild(this._vimage); this._rootObj.appendChild(this._container); this._rootObj.style.position="relative"; // FIXES IE PROBLEM this._rootObj.style.width=width+"px"; this._rootObj.style.height=height+"px"; this._rootObj.setAttribute('id',this._img.getAttribute('id')); this._rootObj.className=this._img.className; this._eventObj = this._rootObj; this._handleRotation(parameters); } else return function (parameters) { this._rootObj.setAttribute('id',this._img.getAttribute('id')); this._rootObj.className=this._img.className; this._width=this._img.width; this._height=this._img.height; this._widthHalf=this._width/2; // used for optimisation this._heightHalf=this._height/2;// used for optimisation var _widthMax=Math.sqrt((this._height)*(this._height) + (this._width) * (this._width)); this._widthAdd = _widthMax - this._width; this._heightAdd = _widthMax - this._height; // widthMax because maxWidth=maxHeight this._widthAddHalf=this._widthAdd/2; // used for optimisation this._heightAddHalf=this._heightAdd/2;// used for optimisation this._img.parentNode.removeChild(this._img); this._aspectW = ((parseInt(this._img.style.width,10)) || this._width)/this._img.width; this._aspectH = ((parseInt(this._img.style.height,10)) || this._height)/this._img.height; this._canvas=document.createElement('canvas'); this._canvas.setAttribute('width',this._width); this._canvas.style.position="relative"; this._canvas.style.left = -this._widthAddHalf + "px"; this._canvas.style.top = -this._heightAddHalf + "px"; this._canvas.Wilq32 = this._rootObj.Wilq32; this._rootObj.appendChild(this._canvas); this._rootObj.style.width=this._width+"px"; this._rootObj.style.height=this._height+"px"; this._eventObj = this._canvas; this._cnv=this._canvas.getContext('2d'); this._handleRotation(parameters); } })(), _animateStart:function() { if (this._timer) { clearTimeout(this._timer); } this._animateStartTime = +new Date; this._animateStartAngle = this._angle; this._animate(); }, _animate:function() { var actualTime = +new Date; var checkEnd = actualTime - this._animateStartTime > this._parameters.duration; // TODO: Bug for animatedGif for static rotation ? (to test) if (checkEnd && !this._parameters.animatedGif) { clearTimeout(this._timer); } else { if (this._canvas||this._vimage||this._img) { var angle = this._parameters.easing(0, actualTime - this._animateStartTime, this._animateStartAngle, this._parameters.animateTo - this._animateStartAngle, this._parameters.duration); this._rotate((~~(angle*10))/10); } if (this._parameters.step) { this._parameters.step(this._angle); } var self = this; this._timer = setTimeout(function() { self._animate.call(self); }, 10); } // To fix Bug that prevents using recursive function in callback I moved this function to back if (this._parameters.callback && checkEnd){ this._angle = this._parameters.animateTo; this._rotate(this._angle); this._parameters.callback.call(this._rootObj); } }, _rotate : (function() { var rad = Math.PI/180; if (IE) return function(angle) { this._angle = angle; this._container.style.rotation=(angle%360)+"deg"; } else if (supportedCSS) return function(angle){ this._angle = angle; this._img.style[supportedCSS]="rotate("+(angle%360)+"deg)"; } else return function(angle) { this._angle = angle; angle=(angle%360)* rad; // clear canvas this._canvas.width = this._width+this._widthAdd; this._canvas.height = this._height+this._heightAdd; // REMEMBER: all drawings are read from backwards.. so first function is translate, then rotate, then translate, translate.. this._cnv.translate(this._widthAddHalf,this._heightAddHalf); // at least center image on screen this._cnv.translate(this._widthHalf,this._heightHalf); // we move image back to its orginal this._cnv.rotate(angle); // rotate image this._cnv.translate(-this._widthHalf,-this._heightHalf); // move image to its center, so we can rotate around its center this._cnv.scale(this._aspectW,this._aspectH); // SCALE - if needed ;) this._cnv.drawImage(this._img, 0, 0); // First - we draw image } })() } if (IE) { Wilq32.PhotoEffect.prototype.createVMLNode=(function(){ document.createStyleSheet().addRule(".rvml", "behavior:url(#default#VML)"); try { !document.namespaces.rvml && document.namespaces.add("rvml", "urn:schemas-microsoft-com:vml"); return function (tagName) { return document.createElement('<rvml:' + tagName + ' class="rvml">'); }; } catch (e) { return function (tagName) { return document.createElement('<' + tagName + ' xmlns="urn:schemas-microsoft.com:vml" class="rvml">'); }; } })(); } })($);
执行的语句插入到component的export Class时,果然之前let的虚假定义露出了马脚:
报错 :“此jquery对象只是一个enhanced的对象。。”
很奇怪的是这个被我let出来require("jquery")的$却是实实在在的获取得到dom对象,还能打印出来,但是对于扩展以后的方法,$().rotate,就会报没有这个方法。我把扩展js放在vendor入口里面,确确实实在调试窗口sources查看了vendor.js包含进去以后,也没还是找不到扩展的方法。
于是我决心再倒腾一下imports-loader,最后可能还是由于angular2.0的版本问题,毕竟不是最新的,这样写,是给所有ts文件导入jquery了:
var webpack = require('webpack'); var autoprefixer = require('autoprefixer'); var HtmlWebpackPlugin = require('html-webpack-plugin'); var ExtractTextPlugin = require('extract-text-webpack-plugin'); module.exports = { //devtool : 'inline-source-map', entry: { main:['./app/main.ts','./app/sass/main.scss'], vendor:[ './dist/assets/js/mobile-base.js', './dist/assets/js/awardRotate.js' ] }, externals:[ { $:"jQuery" }, { jQuery:"jQuery" }, { window: "window"}, { Swiper: "Swiper"}, ], output: { path: './dist', filename: 'js/app.bundle.js', //publicPath:'http://localhost:8000', libraryTarget: "var" }, module: { loaders: [ {test: /.ts$/, loader: 'ts!imports?$=jquery'}, {test: /.html$/, loader: 'raw'}, {test: /.css$/, loader: ExtractTextPlugin.extract('css-loader?sourceMap')}, {test: /.scss$/,
loader: ExtractTextPlugin.extract('css-loader?sourceMap!postcss-loader!sass-loader?outputStyle=compressed')
}, {test: /.(png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot)$/, loader:"url-loader?limit=8192"}, {test: /vendor/.+.(jsx|js)$/, loader: 'imports?this=>window' }
//有的版本!是用,号的语法写错loader是不会有作用呢的,上面这里加上 |ts 貌似也没起作用,费解,总之这个配置是起效的 ] }, ... ... ... ...
imports-loader
https://github.com/webpack/imports-loader
https://github.com/webpack/docs/wiki/shimming-modules
https://github.com/webpack/webpack/issues/542
Modularisation system is a part of every modern language that is used for creating complex applications. javascript is special here - modularisation system is not a part of language itself . We got great standards ( CommonJS or AMD ) and tools to make modularisation work. One of the most popular is Webpack - a module bundler which comes with variety of so-called loaders , transforming the input code. Unfortunately modularisation systems not within the language creates compatibility problems . There are libraries that can’t understand modules and assumes that dependencies are available globally under predefined names. I had this issue today. I wanted to use the great Chosen library for multi-select inputs. Unfortunately Chosen expects to have jQuery defined in the global namespace and is unaware of modules at all. In this article I’d like to show you how to integrate such module-unaware libraries with Webpack without introducing global variables. Goal & Initial State My goal was to allow Chosen to extend my jQuery instance without exposing the jQuery instance as a global. I have jQuery in my project installed using npm install --save jquery command - as a module. So every time I need to use jQuery I need to import it manually (here, using ES2015 syntax): import $ from 'jquery'; Chosen can come as a bower package or as a standalone JS file with some CSS files and images attached. I decided to take the latter approach and put it into a vendor/ folder in my project. So I put the chosen.jquery.js file into the vendor folder. As have been said before, chosen.jquery.js does not understand modules - it needs to have jQuery defined under jQuery name in global namespace. With many libraries like that it is also desirable to have this pointing to the window variable which is not granted in many environments. For example Babel.js assumes that every input it takes is a ES2015 module and this is pointing to null in such case (more about it here ). So if you integrate babel-loader to use ES2015 syntax you may experience problems with such libraries. Having this problem defined, let’s take a look how the perfect solution can be achieved - so have jQuery still as a module without exposing it as a global but being able to use libraries that assumes jQuery is defined as a global . Solution - imports-loader Webpack has a special loader for handling cases like that. It is called imports-loader and you need to install it by yourself - it doesn’t come by default when you install webpack . Fortunately the installation process is straightforward: npm install --save-dev imports-loader Then you need to modify your webpack config. In loaders section of your config you need to add the following code: { test: /vendor/.+.(jsx|js)$/, loader: 'imports?jQuery=jquery,$=jquery,this=>window' } Let’s get through this loader definition. The first part, imports defines that we want to use imports-loader . After ? there is a list of values, separated by a comma. First one jQuery=jquery defines that under the jQuery variable in loaded code there will be a result of require("jquery") expression - in our case, our jQuery library from the module. The second one is the same, but the name is different. If you want to name your variable the same as your module, you can skip the part after the equation sign and the equation sign itself. So jquery would create a jquery variable with require("jquery") value in the loaded code. The last value is about redefining the global variable. So global this will point to window in the loaded code. It is an equivalent of wrapping the whole contents of the file with the function(this) { ... } and calling this function in-place with window as an argument. You can do the same with any global variables and it is indicated by an arrow ( => ). That’s it! Now I can do: import $ from 'jquery'; import "vendor/chosen.jquery"; And I can use chosen jQuery extension without any problems! Summary As you can see, integrating module-unaware libraries without exposing its dependencies as globals is a rather simple task. In fact I thought it’ll be harder - I’ve seen many webpack configurations where popular libraries/frameworks like jQuery or Angular.js were exposed as global variables. I hope that you’ll be able to integrate your favourite libraries that way just like I did with Chosen.
expose-loader
http://blog.csdn.net/yiifaa/article/details/51916560
延伸阅读
webpack中imports-loader,exports-loader,expose-loader的区别
exports-loader
shimming 在 AMD/CMD 中,我们需要对不符合规范的模块(比如一些直接返回全局变量的插件)进行 shim 处理,这时候我们需要使用 exports-loader 来帮忙: { test: require.resolve("./src/js/tool/swipe.js"), loader: "exports?swipe"} 之后在脚本中需要引用该模块的时候,这么简单地来使用就可以了: require('./tool/swipe.js'); swipe();